10508ad1fSJeremy Kerr /* 20508ad1fSJeremy Kerr * FSI core driver 30508ad1fSJeremy Kerr * 40508ad1fSJeremy Kerr * Copyright (C) IBM Corporation 2016 50508ad1fSJeremy Kerr * 60508ad1fSJeremy Kerr * This program is free software; you can redistribute it and/or modify 70508ad1fSJeremy Kerr * it under the terms of the GNU General Public License version 2 as 80508ad1fSJeremy Kerr * published by the Free Software Foundation. 90508ad1fSJeremy Kerr * 100508ad1fSJeremy Kerr * This program is distributed in the hope that it will be useful, 110508ad1fSJeremy Kerr * but WITHOUT ANY WARRANTY; without even the implied warranty of 120508ad1fSJeremy Kerr * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 130508ad1fSJeremy Kerr * GNU General Public License for more details. 140508ad1fSJeremy Kerr */ 150508ad1fSJeremy Kerr 160508ad1fSJeremy Kerr #include <linux/device.h> 170508ad1fSJeremy Kerr #include <linux/fsi.h> 1809aecfabSJeremy Kerr #include <linux/idr.h> 190508ad1fSJeremy Kerr #include <linux/module.h> 200508ad1fSJeremy Kerr 2109aecfabSJeremy Kerr #include "fsi-master.h" 2209aecfabSJeremy Kerr 2309aecfabSJeremy Kerr static DEFINE_IDA(master_ida); 2409aecfabSJeremy Kerr 25faf0b116SJeremy Kerr struct fsi_slave { 26faf0b116SJeremy Kerr struct device dev; 27faf0b116SJeremy Kerr struct fsi_master *master; 28faf0b116SJeremy Kerr int id; 29faf0b116SJeremy Kerr int link; 30faf0b116SJeremy Kerr uint32_t size; /* size of slave address space */ 31faf0b116SJeremy Kerr }; 32faf0b116SJeremy Kerr 33faf0b116SJeremy Kerr #define to_fsi_slave(d) container_of(d, struct fsi_slave, dev) 34faf0b116SJeremy Kerr 35014c2abcSJeremy Kerr static int fsi_master_read(struct fsi_master *master, int link, 36014c2abcSJeremy Kerr uint8_t slave_id, uint32_t addr, void *val, size_t size); 37014c2abcSJeremy Kerr static int fsi_master_write(struct fsi_master *master, int link, 38014c2abcSJeremy Kerr uint8_t slave_id, uint32_t addr, const void *val, size_t size); 39014c2abcSJeremy Kerr 40414c1026SJeremy Kerr /* FSI slave support */ 41014c2abcSJeremy Kerr static int fsi_slave_calc_addr(struct fsi_slave *slave, uint32_t *addrp, 42014c2abcSJeremy Kerr uint8_t *idp) 43014c2abcSJeremy Kerr { 44014c2abcSJeremy Kerr uint32_t addr = *addrp; 45014c2abcSJeremy Kerr uint8_t id = *idp; 46014c2abcSJeremy Kerr 47014c2abcSJeremy Kerr if (addr > slave->size) 48014c2abcSJeremy Kerr return -EINVAL; 49014c2abcSJeremy Kerr 50014c2abcSJeremy Kerr /* For 23 bit addressing, we encode the extra two bits in the slave 51014c2abcSJeremy Kerr * id (and the slave's actual ID needs to be 0). 52014c2abcSJeremy Kerr */ 53014c2abcSJeremy Kerr if (addr > 0x1fffff) { 54014c2abcSJeremy Kerr if (slave->id != 0) 55014c2abcSJeremy Kerr return -EINVAL; 56014c2abcSJeremy Kerr id = (addr >> 21) & 0x3; 57014c2abcSJeremy Kerr addr &= 0x1fffff; 58014c2abcSJeremy Kerr } 59014c2abcSJeremy Kerr 60014c2abcSJeremy Kerr *addrp = addr; 61014c2abcSJeremy Kerr *idp = id; 62014c2abcSJeremy Kerr return 0; 63014c2abcSJeremy Kerr } 64014c2abcSJeremy Kerr 65014c2abcSJeremy Kerr static int fsi_slave_read(struct fsi_slave *slave, uint32_t addr, 66014c2abcSJeremy Kerr void *val, size_t size) 67014c2abcSJeremy Kerr { 68014c2abcSJeremy Kerr uint8_t id = slave->id; 69014c2abcSJeremy Kerr int rc; 70014c2abcSJeremy Kerr 71014c2abcSJeremy Kerr rc = fsi_slave_calc_addr(slave, &addr, &id); 72014c2abcSJeremy Kerr if (rc) 73014c2abcSJeremy Kerr return rc; 74014c2abcSJeremy Kerr 75014c2abcSJeremy Kerr return fsi_master_read(slave->master, slave->link, id, 76014c2abcSJeremy Kerr addr, val, size); 77014c2abcSJeremy Kerr } 78014c2abcSJeremy Kerr 79014c2abcSJeremy Kerr static int fsi_slave_write(struct fsi_slave *slave, uint32_t addr, 80014c2abcSJeremy Kerr const void *val, size_t size) 81014c2abcSJeremy Kerr { 82014c2abcSJeremy Kerr uint8_t id = slave->id; 83014c2abcSJeremy Kerr int rc; 84014c2abcSJeremy Kerr 85014c2abcSJeremy Kerr rc = fsi_slave_calc_addr(slave, &addr, &id); 86014c2abcSJeremy Kerr if (rc) 87014c2abcSJeremy Kerr return rc; 88014c2abcSJeremy Kerr 89014c2abcSJeremy Kerr return fsi_master_write(slave->master, slave->link, id, 90014c2abcSJeremy Kerr addr, val, size); 91014c2abcSJeremy Kerr } 92014c2abcSJeremy Kerr 93414c1026SJeremy Kerr static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id) 94414c1026SJeremy Kerr { 95414c1026SJeremy Kerr /* todo: initialise slave device, perform engine scan */ 96414c1026SJeremy Kerr 97414c1026SJeremy Kerr return -ENODEV; 98414c1026SJeremy Kerr } 99414c1026SJeremy Kerr 10009aecfabSJeremy Kerr /* FSI master support */ 101014c2abcSJeremy Kerr static int fsi_check_access(uint32_t addr, size_t size) 102014c2abcSJeremy Kerr { 103014c2abcSJeremy Kerr if (size != 1 && size != 2 && size != 4) 104014c2abcSJeremy Kerr return -EINVAL; 105014c2abcSJeremy Kerr 106014c2abcSJeremy Kerr if ((addr & 0x3) != (size & 0x3)) 107014c2abcSJeremy Kerr return -EINVAL; 108014c2abcSJeremy Kerr 109014c2abcSJeremy Kerr return 0; 110014c2abcSJeremy Kerr } 111014c2abcSJeremy Kerr 112014c2abcSJeremy Kerr static int fsi_master_read(struct fsi_master *master, int link, 113014c2abcSJeremy Kerr uint8_t slave_id, uint32_t addr, void *val, size_t size) 114014c2abcSJeremy Kerr { 115014c2abcSJeremy Kerr int rc; 116014c2abcSJeremy Kerr 117014c2abcSJeremy Kerr rc = fsi_check_access(addr, size); 118014c2abcSJeremy Kerr if (rc) 119014c2abcSJeremy Kerr return rc; 120014c2abcSJeremy Kerr 121014c2abcSJeremy Kerr return master->read(master, link, slave_id, addr, val, size); 122014c2abcSJeremy Kerr } 123014c2abcSJeremy Kerr 124014c2abcSJeremy Kerr static int fsi_master_write(struct fsi_master *master, int link, 125014c2abcSJeremy Kerr uint8_t slave_id, uint32_t addr, const void *val, size_t size) 126014c2abcSJeremy Kerr { 127014c2abcSJeremy Kerr int rc; 128014c2abcSJeremy Kerr 129014c2abcSJeremy Kerr rc = fsi_check_access(addr, size); 130014c2abcSJeremy Kerr if (rc) 131014c2abcSJeremy Kerr return rc; 132014c2abcSJeremy Kerr 133014c2abcSJeremy Kerr return master->write(master, link, slave_id, addr, val, size); 134014c2abcSJeremy Kerr } 135014c2abcSJeremy Kerr 136414c1026SJeremy Kerr static int fsi_master_scan(struct fsi_master *master) 137414c1026SJeremy Kerr { 138414c1026SJeremy Kerr int link; 139414c1026SJeremy Kerr 140414c1026SJeremy Kerr for (link = 0; link < master->n_links; link++) 141414c1026SJeremy Kerr fsi_slave_init(master, link, 0); 142414c1026SJeremy Kerr 143414c1026SJeremy Kerr return 0; 144414c1026SJeremy Kerr } 145414c1026SJeremy Kerr 14609aecfabSJeremy Kerr int fsi_master_register(struct fsi_master *master) 14709aecfabSJeremy Kerr { 14809aecfabSJeremy Kerr int rc; 14909aecfabSJeremy Kerr 15009aecfabSJeremy Kerr if (!master) 15109aecfabSJeremy Kerr return -EINVAL; 15209aecfabSJeremy Kerr 15309aecfabSJeremy Kerr master->idx = ida_simple_get(&master_ida, 0, INT_MAX, GFP_KERNEL); 15409aecfabSJeremy Kerr dev_set_name(&master->dev, "fsi%d", master->idx); 15509aecfabSJeremy Kerr 15609aecfabSJeremy Kerr rc = device_register(&master->dev); 157414c1026SJeremy Kerr if (rc) { 15809aecfabSJeremy Kerr ida_simple_remove(&master_ida, master->idx); 15909aecfabSJeremy Kerr return rc; 16009aecfabSJeremy Kerr } 161414c1026SJeremy Kerr 162414c1026SJeremy Kerr fsi_master_scan(master); 163414c1026SJeremy Kerr return 0; 164414c1026SJeremy Kerr } 16509aecfabSJeremy Kerr EXPORT_SYMBOL_GPL(fsi_master_register); 16609aecfabSJeremy Kerr 16709aecfabSJeremy Kerr void fsi_master_unregister(struct fsi_master *master) 16809aecfabSJeremy Kerr { 16909aecfabSJeremy Kerr if (master->idx >= 0) { 17009aecfabSJeremy Kerr ida_simple_remove(&master_ida, master->idx); 17109aecfabSJeremy Kerr master->idx = -1; 17209aecfabSJeremy Kerr } 17309aecfabSJeremy Kerr 17409aecfabSJeremy Kerr device_unregister(&master->dev); 17509aecfabSJeremy Kerr } 17609aecfabSJeremy Kerr EXPORT_SYMBOL_GPL(fsi_master_unregister); 17709aecfabSJeremy Kerr 1780508ad1fSJeremy Kerr /* FSI core & Linux bus type definitions */ 1790508ad1fSJeremy Kerr 180dd37eed7SJeremy Kerr static int fsi_bus_match(struct device *dev, struct device_driver *drv) 181dd37eed7SJeremy Kerr { 182dd37eed7SJeremy Kerr struct fsi_device *fsi_dev = to_fsi_dev(dev); 183dd37eed7SJeremy Kerr struct fsi_driver *fsi_drv = to_fsi_drv(drv); 184dd37eed7SJeremy Kerr const struct fsi_device_id *id; 185dd37eed7SJeremy Kerr 186dd37eed7SJeremy Kerr if (!fsi_drv->id_table) 187dd37eed7SJeremy Kerr return 0; 188dd37eed7SJeremy Kerr 189dd37eed7SJeremy Kerr for (id = fsi_drv->id_table; id->engine_type; id++) { 190dd37eed7SJeremy Kerr if (id->engine_type != fsi_dev->engine_type) 191dd37eed7SJeremy Kerr continue; 192dd37eed7SJeremy Kerr if (id->version == FSI_VERSION_ANY || 193dd37eed7SJeremy Kerr id->version == fsi_dev->version) 194dd37eed7SJeremy Kerr return 1; 195dd37eed7SJeremy Kerr } 196dd37eed7SJeremy Kerr 197dd37eed7SJeremy Kerr return 0; 198dd37eed7SJeremy Kerr } 199dd37eed7SJeremy Kerr 2000508ad1fSJeremy Kerr struct bus_type fsi_bus_type = { 2010508ad1fSJeremy Kerr .name = "fsi", 202dd37eed7SJeremy Kerr .match = fsi_bus_match, 2030508ad1fSJeremy Kerr }; 2040508ad1fSJeremy Kerr EXPORT_SYMBOL_GPL(fsi_bus_type); 2050508ad1fSJeremy Kerr 2060508ad1fSJeremy Kerr static int fsi_init(void) 2070508ad1fSJeremy Kerr { 2080508ad1fSJeremy Kerr return bus_register(&fsi_bus_type); 2090508ad1fSJeremy Kerr } 2100508ad1fSJeremy Kerr 2110508ad1fSJeremy Kerr static void fsi_exit(void) 2120508ad1fSJeremy Kerr { 2130508ad1fSJeremy Kerr bus_unregister(&fsi_bus_type); 2140508ad1fSJeremy Kerr } 2150508ad1fSJeremy Kerr 2160508ad1fSJeremy Kerr module_init(fsi_init); 2170508ad1fSJeremy Kerr module_exit(fsi_exit); 218