18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * FSI core driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) IBM Corporation 2016 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * TODO: 88c2ecf20Sopenharmony_ci * - Rework topology 98c2ecf20Sopenharmony_ci * - s/chip_id/chip_loc 108c2ecf20Sopenharmony_ci * - s/cfam/chip (cfam_id -> chip_id etc...) 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/crc4.h> 148c2ecf20Sopenharmony_ci#include <linux/device.h> 158c2ecf20Sopenharmony_ci#include <linux/fsi.h> 168c2ecf20Sopenharmony_ci#include <linux/idr.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/of.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/bitops.h> 218c2ecf20Sopenharmony_ci#include <linux/cdev.h> 228c2ecf20Sopenharmony_ci#include <linux/fs.h> 238c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "fsi-master.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS 288c2ecf20Sopenharmony_ci#include <trace/events/fsi.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define FSI_SLAVE_CONF_NEXT_MASK GENMASK(31, 31) 318c2ecf20Sopenharmony_ci#define FSI_SLAVE_CONF_SLOTS_MASK GENMASK(23, 16) 328c2ecf20Sopenharmony_ci#define FSI_SLAVE_CONF_SLOTS_SHIFT 16 338c2ecf20Sopenharmony_ci#define FSI_SLAVE_CONF_VERSION_MASK GENMASK(15, 12) 348c2ecf20Sopenharmony_ci#define FSI_SLAVE_CONF_VERSION_SHIFT 12 358c2ecf20Sopenharmony_ci#define FSI_SLAVE_CONF_TYPE_MASK GENMASK(11, 4) 368c2ecf20Sopenharmony_ci#define FSI_SLAVE_CONF_TYPE_SHIFT 4 378c2ecf20Sopenharmony_ci#define FSI_SLAVE_CONF_CRC_SHIFT 4 388c2ecf20Sopenharmony_ci#define FSI_SLAVE_CONF_CRC_MASK GENMASK(3, 0) 398c2ecf20Sopenharmony_ci#define FSI_SLAVE_CONF_DATA_BITS 28 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define FSI_PEEK_BASE 0x410 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic const int engine_page_size = 0x400; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define FSI_SLAVE_BASE 0x800 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* 488c2ecf20Sopenharmony_ci * FSI slave engine control register offsets 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci#define FSI_SMODE 0x0 /* R/W: Mode register */ 518c2ecf20Sopenharmony_ci#define FSI_SISC 0x8 /* R/W: Interrupt condition */ 528c2ecf20Sopenharmony_ci#define FSI_SSTAT 0x14 /* R : Slave status */ 538c2ecf20Sopenharmony_ci#define FSI_SLBUS 0x30 /* W : LBUS Ownership */ 548c2ecf20Sopenharmony_ci#define FSI_LLMODE 0x100 /* R/W: Link layer mode register */ 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* 578c2ecf20Sopenharmony_ci * SMODE fields 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_ci#define FSI_SMODE_WSC 0x80000000 /* Warm start done */ 608c2ecf20Sopenharmony_ci#define FSI_SMODE_ECRC 0x20000000 /* Hw CRC check */ 618c2ecf20Sopenharmony_ci#define FSI_SMODE_SID_SHIFT 24 /* ID shift */ 628c2ecf20Sopenharmony_ci#define FSI_SMODE_SID_MASK 3 /* ID Mask */ 638c2ecf20Sopenharmony_ci#define FSI_SMODE_ED_SHIFT 20 /* Echo delay shift */ 648c2ecf20Sopenharmony_ci#define FSI_SMODE_ED_MASK 0xf /* Echo delay mask */ 658c2ecf20Sopenharmony_ci#define FSI_SMODE_SD_SHIFT 16 /* Send delay shift */ 668c2ecf20Sopenharmony_ci#define FSI_SMODE_SD_MASK 0xf /* Send delay mask */ 678c2ecf20Sopenharmony_ci#define FSI_SMODE_LBCRR_SHIFT 8 /* Clk ratio shift */ 688c2ecf20Sopenharmony_ci#define FSI_SMODE_LBCRR_MASK 0xf /* Clk ratio mask */ 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* 718c2ecf20Sopenharmony_ci * SLBUS fields 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_ci#define FSI_SLBUS_FORCE 0x80000000 /* Force LBUS ownership */ 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* 768c2ecf20Sopenharmony_ci * LLMODE fields 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_ci#define FSI_LLMODE_ASYNC 0x1 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define FSI_SLAVE_SIZE_23b 0x800000 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic DEFINE_IDA(master_ida); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistruct fsi_slave { 858c2ecf20Sopenharmony_ci struct device dev; 868c2ecf20Sopenharmony_ci struct fsi_master *master; 878c2ecf20Sopenharmony_ci struct cdev cdev; 888c2ecf20Sopenharmony_ci int cdev_idx; 898c2ecf20Sopenharmony_ci int id; /* FSI address */ 908c2ecf20Sopenharmony_ci int link; /* FSI link# */ 918c2ecf20Sopenharmony_ci u32 cfam_id; 928c2ecf20Sopenharmony_ci int chip_id; 938c2ecf20Sopenharmony_ci uint32_t size; /* size of slave address space */ 948c2ecf20Sopenharmony_ci u8 t_send_delay; 958c2ecf20Sopenharmony_ci u8 t_echo_delay; 968c2ecf20Sopenharmony_ci}; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#define to_fsi_master(d) container_of(d, struct fsi_master, dev) 998c2ecf20Sopenharmony_ci#define to_fsi_slave(d) container_of(d, struct fsi_slave, dev) 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic const int slave_retries = 2; 1028c2ecf20Sopenharmony_cistatic int discard_errors; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic dev_t fsi_base_dev; 1058c2ecf20Sopenharmony_cistatic DEFINE_IDA(fsi_minor_ida); 1068c2ecf20Sopenharmony_ci#define FSI_CHAR_MAX_DEVICES 0x1000 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* Legacy /dev numbering: 4 devices per chip, 16 chips */ 1098c2ecf20Sopenharmony_ci#define FSI_CHAR_LEGACY_TOP 64 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic int fsi_master_read(struct fsi_master *master, int link, 1128c2ecf20Sopenharmony_ci uint8_t slave_id, uint32_t addr, void *val, size_t size); 1138c2ecf20Sopenharmony_cistatic int fsi_master_write(struct fsi_master *master, int link, 1148c2ecf20Sopenharmony_ci uint8_t slave_id, uint32_t addr, const void *val, size_t size); 1158c2ecf20Sopenharmony_cistatic int fsi_master_break(struct fsi_master *master, int link); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/* 1188c2ecf20Sopenharmony_ci * fsi_device_read() / fsi_device_write() / fsi_device_peek() 1198c2ecf20Sopenharmony_ci * 1208c2ecf20Sopenharmony_ci * FSI endpoint-device support 1218c2ecf20Sopenharmony_ci * 1228c2ecf20Sopenharmony_ci * Read / write / peek accessors for a client 1238c2ecf20Sopenharmony_ci * 1248c2ecf20Sopenharmony_ci * Parameters: 1258c2ecf20Sopenharmony_ci * dev: Structure passed to FSI client device drivers on probe(). 1268c2ecf20Sopenharmony_ci * addr: FSI address of given device. Client should pass in its base address 1278c2ecf20Sopenharmony_ci * plus desired offset to access its register space. 1288c2ecf20Sopenharmony_ci * val: For read/peek this is the value read at the specified address. For 1298c2ecf20Sopenharmony_ci * write this is value to write to the specified address. 1308c2ecf20Sopenharmony_ci * The data in val must be FSI bus endian (big endian). 1318c2ecf20Sopenharmony_ci * size: Size in bytes of the operation. Sizes supported are 1, 2 and 4 bytes. 1328c2ecf20Sopenharmony_ci * Addresses must be aligned on size boundaries or an error will result. 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_ciint fsi_device_read(struct fsi_device *dev, uint32_t addr, void *val, 1358c2ecf20Sopenharmony_ci size_t size) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci if (addr > dev->size || size > dev->size || addr > dev->size - size) 1388c2ecf20Sopenharmony_ci return -EINVAL; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return fsi_slave_read(dev->slave, dev->addr + addr, val, size); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_device_read); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ciint fsi_device_write(struct fsi_device *dev, uint32_t addr, const void *val, 1458c2ecf20Sopenharmony_ci size_t size) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci if (addr > dev->size || size > dev->size || addr > dev->size - size) 1488c2ecf20Sopenharmony_ci return -EINVAL; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return fsi_slave_write(dev->slave, dev->addr + addr, val, size); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_device_write); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ciint fsi_device_peek(struct fsi_device *dev, void *val) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci uint32_t addr = FSI_PEEK_BASE + ((dev->unit - 2) * sizeof(uint32_t)); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return fsi_slave_read(dev->slave, addr, val, sizeof(uint32_t)); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic void fsi_device_release(struct device *_device) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci struct fsi_device *device = to_fsi_dev(_device); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci of_node_put(device->dev.of_node); 1668c2ecf20Sopenharmony_ci kfree(device); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic struct fsi_device *fsi_create_device(struct fsi_slave *slave) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct fsi_device *dev; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 1748c2ecf20Sopenharmony_ci if (!dev) 1758c2ecf20Sopenharmony_ci return NULL; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci dev->dev.parent = &slave->dev; 1788c2ecf20Sopenharmony_ci dev->dev.bus = &fsi_bus_type; 1798c2ecf20Sopenharmony_ci dev->dev.release = fsi_device_release; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return dev; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci/* FSI slave support */ 1858c2ecf20Sopenharmony_cistatic int fsi_slave_calc_addr(struct fsi_slave *slave, uint32_t *addrp, 1868c2ecf20Sopenharmony_ci uint8_t *idp) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci uint32_t addr = *addrp; 1898c2ecf20Sopenharmony_ci uint8_t id = *idp; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (addr > slave->size) 1928c2ecf20Sopenharmony_ci return -EINVAL; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* For 23 bit addressing, we encode the extra two bits in the slave 1958c2ecf20Sopenharmony_ci * id (and the slave's actual ID needs to be 0). 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_ci if (addr > 0x1fffff) { 1988c2ecf20Sopenharmony_ci if (slave->id != 0) 1998c2ecf20Sopenharmony_ci return -EINVAL; 2008c2ecf20Sopenharmony_ci id = (addr >> 21) & 0x3; 2018c2ecf20Sopenharmony_ci addr &= 0x1fffff; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci *addrp = addr; 2058c2ecf20Sopenharmony_ci *idp = id; 2068c2ecf20Sopenharmony_ci return 0; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic int fsi_slave_report_and_clear_errors(struct fsi_slave *slave) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct fsi_master *master = slave->master; 2128c2ecf20Sopenharmony_ci __be32 irq, stat; 2138c2ecf20Sopenharmony_ci int rc, link; 2148c2ecf20Sopenharmony_ci uint8_t id; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci link = slave->link; 2178c2ecf20Sopenharmony_ci id = slave->id; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci rc = fsi_master_read(master, link, id, FSI_SLAVE_BASE + FSI_SISC, 2208c2ecf20Sopenharmony_ci &irq, sizeof(irq)); 2218c2ecf20Sopenharmony_ci if (rc) 2228c2ecf20Sopenharmony_ci return rc; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci rc = fsi_master_read(master, link, id, FSI_SLAVE_BASE + FSI_SSTAT, 2258c2ecf20Sopenharmony_ci &stat, sizeof(stat)); 2268c2ecf20Sopenharmony_ci if (rc) 2278c2ecf20Sopenharmony_ci return rc; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci dev_dbg(&slave->dev, "status: 0x%08x, sisc: 0x%08x\n", 2308c2ecf20Sopenharmony_ci be32_to_cpu(stat), be32_to_cpu(irq)); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* clear interrupts */ 2338c2ecf20Sopenharmony_ci return fsi_master_write(master, link, id, FSI_SLAVE_BASE + FSI_SISC, 2348c2ecf20Sopenharmony_ci &irq, sizeof(irq)); 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci/* Encode slave local bus echo delay */ 2388c2ecf20Sopenharmony_cistatic inline uint32_t fsi_smode_echodly(int x) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci return (x & FSI_SMODE_ED_MASK) << FSI_SMODE_ED_SHIFT; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci/* Encode slave local bus send delay */ 2448c2ecf20Sopenharmony_cistatic inline uint32_t fsi_smode_senddly(int x) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci return (x & FSI_SMODE_SD_MASK) << FSI_SMODE_SD_SHIFT; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci/* Encode slave local bus clock rate ratio */ 2508c2ecf20Sopenharmony_cistatic inline uint32_t fsi_smode_lbcrr(int x) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci return (x & FSI_SMODE_LBCRR_MASK) << FSI_SMODE_LBCRR_SHIFT; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci/* Encode slave ID */ 2568c2ecf20Sopenharmony_cistatic inline uint32_t fsi_smode_sid(int x) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci return (x & FSI_SMODE_SID_MASK) << FSI_SMODE_SID_SHIFT; 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic uint32_t fsi_slave_smode(int id, u8 t_senddly, u8 t_echodly) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci return FSI_SMODE_WSC | FSI_SMODE_ECRC 2648c2ecf20Sopenharmony_ci | fsi_smode_sid(id) 2658c2ecf20Sopenharmony_ci | fsi_smode_echodly(t_echodly - 1) | fsi_smode_senddly(t_senddly - 1) 2668c2ecf20Sopenharmony_ci | fsi_smode_lbcrr(0x8); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic int fsi_slave_set_smode(struct fsi_slave *slave) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci uint32_t smode; 2728c2ecf20Sopenharmony_ci __be32 data; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* set our smode register with the slave ID field to 0; this enables 2758c2ecf20Sopenharmony_ci * extended slave addressing 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_ci smode = fsi_slave_smode(slave->id, slave->t_send_delay, slave->t_echo_delay); 2788c2ecf20Sopenharmony_ci data = cpu_to_be32(smode); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return fsi_master_write(slave->master, slave->link, slave->id, 2818c2ecf20Sopenharmony_ci FSI_SLAVE_BASE + FSI_SMODE, 2828c2ecf20Sopenharmony_ci &data, sizeof(data)); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic int fsi_slave_handle_error(struct fsi_slave *slave, bool write, 2868c2ecf20Sopenharmony_ci uint32_t addr, size_t size) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct fsi_master *master = slave->master; 2898c2ecf20Sopenharmony_ci int rc, link; 2908c2ecf20Sopenharmony_ci uint32_t reg; 2918c2ecf20Sopenharmony_ci uint8_t id, send_delay, echo_delay; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (discard_errors) 2948c2ecf20Sopenharmony_ci return -1; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci link = slave->link; 2978c2ecf20Sopenharmony_ci id = slave->id; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci dev_dbg(&slave->dev, "handling error on %s to 0x%08x[%zd]", 3008c2ecf20Sopenharmony_ci write ? "write" : "read", addr, size); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* try a simple clear of error conditions, which may fail if we've lost 3038c2ecf20Sopenharmony_ci * communication with the slave 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_ci rc = fsi_slave_report_and_clear_errors(slave); 3068c2ecf20Sopenharmony_ci if (!rc) 3078c2ecf20Sopenharmony_ci return 0; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* send a TERM and retry */ 3108c2ecf20Sopenharmony_ci if (master->term) { 3118c2ecf20Sopenharmony_ci rc = master->term(master, link, id); 3128c2ecf20Sopenharmony_ci if (!rc) { 3138c2ecf20Sopenharmony_ci rc = fsi_master_read(master, link, id, 0, 3148c2ecf20Sopenharmony_ci ®, sizeof(reg)); 3158c2ecf20Sopenharmony_ci if (!rc) 3168c2ecf20Sopenharmony_ci rc = fsi_slave_report_and_clear_errors(slave); 3178c2ecf20Sopenharmony_ci if (!rc) 3188c2ecf20Sopenharmony_ci return 0; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci send_delay = slave->t_send_delay; 3238c2ecf20Sopenharmony_ci echo_delay = slave->t_echo_delay; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* getting serious, reset the slave via BREAK */ 3268c2ecf20Sopenharmony_ci rc = fsi_master_break(master, link); 3278c2ecf20Sopenharmony_ci if (rc) 3288c2ecf20Sopenharmony_ci return rc; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci slave->t_send_delay = send_delay; 3318c2ecf20Sopenharmony_ci slave->t_echo_delay = echo_delay; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci rc = fsi_slave_set_smode(slave); 3348c2ecf20Sopenharmony_ci if (rc) 3358c2ecf20Sopenharmony_ci return rc; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (master->link_config) 3388c2ecf20Sopenharmony_ci master->link_config(master, link, 3398c2ecf20Sopenharmony_ci slave->t_send_delay, 3408c2ecf20Sopenharmony_ci slave->t_echo_delay); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci return fsi_slave_report_and_clear_errors(slave); 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ciint fsi_slave_read(struct fsi_slave *slave, uint32_t addr, 3468c2ecf20Sopenharmony_ci void *val, size_t size) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci uint8_t id = slave->id; 3498c2ecf20Sopenharmony_ci int rc, err_rc, i; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci rc = fsi_slave_calc_addr(slave, &addr, &id); 3528c2ecf20Sopenharmony_ci if (rc) 3538c2ecf20Sopenharmony_ci return rc; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci for (i = 0; i < slave_retries; i++) { 3568c2ecf20Sopenharmony_ci rc = fsi_master_read(slave->master, slave->link, 3578c2ecf20Sopenharmony_ci id, addr, val, size); 3588c2ecf20Sopenharmony_ci if (!rc) 3598c2ecf20Sopenharmony_ci break; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci err_rc = fsi_slave_handle_error(slave, false, addr, size); 3628c2ecf20Sopenharmony_ci if (err_rc) 3638c2ecf20Sopenharmony_ci break; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci return rc; 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_slave_read); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ciint fsi_slave_write(struct fsi_slave *slave, uint32_t addr, 3718c2ecf20Sopenharmony_ci const void *val, size_t size) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci uint8_t id = slave->id; 3748c2ecf20Sopenharmony_ci int rc, err_rc, i; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci rc = fsi_slave_calc_addr(slave, &addr, &id); 3778c2ecf20Sopenharmony_ci if (rc) 3788c2ecf20Sopenharmony_ci return rc; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci for (i = 0; i < slave_retries; i++) { 3818c2ecf20Sopenharmony_ci rc = fsi_master_write(slave->master, slave->link, 3828c2ecf20Sopenharmony_ci id, addr, val, size); 3838c2ecf20Sopenharmony_ci if (!rc) 3848c2ecf20Sopenharmony_ci break; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci err_rc = fsi_slave_handle_error(slave, true, addr, size); 3878c2ecf20Sopenharmony_ci if (err_rc) 3888c2ecf20Sopenharmony_ci break; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci return rc; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_slave_write); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ciextern int fsi_slave_claim_range(struct fsi_slave *slave, 3968c2ecf20Sopenharmony_ci uint32_t addr, uint32_t size) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci if (addr + size < addr) 3998c2ecf20Sopenharmony_ci return -EINVAL; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (addr + size > slave->size) 4028c2ecf20Sopenharmony_ci return -EINVAL; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* todo: check for overlapping claims */ 4058c2ecf20Sopenharmony_ci return 0; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_slave_claim_range); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ciextern void fsi_slave_release_range(struct fsi_slave *slave, 4108c2ecf20Sopenharmony_ci uint32_t addr, uint32_t size) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_slave_release_range); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic bool fsi_device_node_matches(struct device *dev, struct device_node *np, 4168c2ecf20Sopenharmony_ci uint32_t addr, uint32_t size) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci unsigned int len, na, ns; 4198c2ecf20Sopenharmony_ci const __be32 *prop; 4208c2ecf20Sopenharmony_ci uint32_t psize; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci na = of_n_addr_cells(np); 4238c2ecf20Sopenharmony_ci ns = of_n_size_cells(np); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (na != 1 || ns != 1) 4268c2ecf20Sopenharmony_ci return false; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci prop = of_get_property(np, "reg", &len); 4298c2ecf20Sopenharmony_ci if (!prop || len != 8) 4308c2ecf20Sopenharmony_ci return false; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci if (of_read_number(prop, 1) != addr) 4338c2ecf20Sopenharmony_ci return false; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci psize = of_read_number(prop + 1, 1); 4368c2ecf20Sopenharmony_ci if (psize != size) { 4378c2ecf20Sopenharmony_ci dev_warn(dev, 4388c2ecf20Sopenharmony_ci "node %s matches probed address, but not size (got 0x%x, expected 0x%x)", 4398c2ecf20Sopenharmony_ci of_node_full_name(np), psize, size); 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci return true; 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci/* Find a matching node for the slave engine at @address, using @size bytes 4468c2ecf20Sopenharmony_ci * of space. Returns NULL if not found, or a matching node with refcount 4478c2ecf20Sopenharmony_ci * already incremented. 4488c2ecf20Sopenharmony_ci */ 4498c2ecf20Sopenharmony_cistatic struct device_node *fsi_device_find_of_node(struct fsi_device *dev) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci struct device_node *parent, *np; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci parent = dev_of_node(&dev->slave->dev); 4548c2ecf20Sopenharmony_ci if (!parent) 4558c2ecf20Sopenharmony_ci return NULL; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci for_each_child_of_node(parent, np) { 4588c2ecf20Sopenharmony_ci if (fsi_device_node_matches(&dev->dev, np, 4598c2ecf20Sopenharmony_ci dev->addr, dev->size)) 4608c2ecf20Sopenharmony_ci return np; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci return NULL; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic int fsi_slave_scan(struct fsi_slave *slave) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci uint32_t engine_addr; 4698c2ecf20Sopenharmony_ci int rc, i; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* 4728c2ecf20Sopenharmony_ci * scan engines 4738c2ecf20Sopenharmony_ci * 4748c2ecf20Sopenharmony_ci * We keep the peek mode and slave engines for the core; so start 4758c2ecf20Sopenharmony_ci * at the third slot in the configuration table. We also need to 4768c2ecf20Sopenharmony_ci * skip the chip ID entry at the start of the address space. 4778c2ecf20Sopenharmony_ci */ 4788c2ecf20Sopenharmony_ci engine_addr = engine_page_size * 3; 4798c2ecf20Sopenharmony_ci for (i = 2; i < engine_page_size / sizeof(uint32_t); i++) { 4808c2ecf20Sopenharmony_ci uint8_t slots, version, type, crc; 4818c2ecf20Sopenharmony_ci struct fsi_device *dev; 4828c2ecf20Sopenharmony_ci uint32_t conf; 4838c2ecf20Sopenharmony_ci __be32 data; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci rc = fsi_slave_read(slave, (i + 1) * sizeof(data), 4868c2ecf20Sopenharmony_ci &data, sizeof(data)); 4878c2ecf20Sopenharmony_ci if (rc) { 4888c2ecf20Sopenharmony_ci dev_warn(&slave->dev, 4898c2ecf20Sopenharmony_ci "error reading slave registers\n"); 4908c2ecf20Sopenharmony_ci return -1; 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci conf = be32_to_cpu(data); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci crc = crc4(0, conf, 32); 4958c2ecf20Sopenharmony_ci if (crc) { 4968c2ecf20Sopenharmony_ci dev_warn(&slave->dev, 4978c2ecf20Sopenharmony_ci "crc error in slave register at 0x%04x\n", 4988c2ecf20Sopenharmony_ci i); 4998c2ecf20Sopenharmony_ci return -1; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci slots = (conf & FSI_SLAVE_CONF_SLOTS_MASK) 5038c2ecf20Sopenharmony_ci >> FSI_SLAVE_CONF_SLOTS_SHIFT; 5048c2ecf20Sopenharmony_ci version = (conf & FSI_SLAVE_CONF_VERSION_MASK) 5058c2ecf20Sopenharmony_ci >> FSI_SLAVE_CONF_VERSION_SHIFT; 5068c2ecf20Sopenharmony_ci type = (conf & FSI_SLAVE_CONF_TYPE_MASK) 5078c2ecf20Sopenharmony_ci >> FSI_SLAVE_CONF_TYPE_SHIFT; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* 5108c2ecf20Sopenharmony_ci * Unused address areas are marked by a zero type value; this 5118c2ecf20Sopenharmony_ci * skips the defined address areas 5128c2ecf20Sopenharmony_ci */ 5138c2ecf20Sopenharmony_ci if (type != 0 && slots != 0) { 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* create device */ 5168c2ecf20Sopenharmony_ci dev = fsi_create_device(slave); 5178c2ecf20Sopenharmony_ci if (!dev) 5188c2ecf20Sopenharmony_ci return -ENOMEM; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci dev->slave = slave; 5218c2ecf20Sopenharmony_ci dev->engine_type = type; 5228c2ecf20Sopenharmony_ci dev->version = version; 5238c2ecf20Sopenharmony_ci dev->unit = i; 5248c2ecf20Sopenharmony_ci dev->addr = engine_addr; 5258c2ecf20Sopenharmony_ci dev->size = slots * engine_page_size; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci dev_dbg(&slave->dev, 5288c2ecf20Sopenharmony_ci "engine[%i]: type %x, version %x, addr %x size %x\n", 5298c2ecf20Sopenharmony_ci dev->unit, dev->engine_type, version, 5308c2ecf20Sopenharmony_ci dev->addr, dev->size); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci dev_set_name(&dev->dev, "%02x:%02x:%02x:%02x", 5338c2ecf20Sopenharmony_ci slave->master->idx, slave->link, 5348c2ecf20Sopenharmony_ci slave->id, i - 2); 5358c2ecf20Sopenharmony_ci dev->dev.of_node = fsi_device_find_of_node(dev); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci rc = device_register(&dev->dev); 5388c2ecf20Sopenharmony_ci if (rc) { 5398c2ecf20Sopenharmony_ci dev_warn(&slave->dev, "add failed: %d\n", rc); 5408c2ecf20Sopenharmony_ci put_device(&dev->dev); 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci engine_addr += slots * engine_page_size; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (!(conf & FSI_SLAVE_CONF_NEXT_MASK)) 5478c2ecf20Sopenharmony_ci break; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci return 0; 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic unsigned long aligned_access_size(size_t offset, size_t count) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci unsigned long offset_unit, count_unit; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* Criteria: 5588c2ecf20Sopenharmony_ci * 5598c2ecf20Sopenharmony_ci * 1. Access size must be less than or equal to the maximum access 5608c2ecf20Sopenharmony_ci * width or the highest power-of-two factor of offset 5618c2ecf20Sopenharmony_ci * 2. Access size must be less than or equal to the amount specified by 5628c2ecf20Sopenharmony_ci * count 5638c2ecf20Sopenharmony_ci * 5648c2ecf20Sopenharmony_ci * The access width is optimal if we can calculate 1 to be strictly 5658c2ecf20Sopenharmony_ci * equal while still satisfying 2. 5668c2ecf20Sopenharmony_ci */ 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci /* Find 1 by the bottom bit of offset (with a 4 byte access cap) */ 5698c2ecf20Sopenharmony_ci offset_unit = BIT(__builtin_ctzl(offset | 4)); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* Find 2 by the top bit of count */ 5728c2ecf20Sopenharmony_ci count_unit = BIT(8 * sizeof(unsigned long) - 1 - __builtin_clzl(count)); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* Constrain the maximum access width to the minimum of both criteria */ 5758c2ecf20Sopenharmony_ci return BIT(__builtin_ctzl(offset_unit | count_unit)); 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cistatic ssize_t fsi_slave_sysfs_raw_read(struct file *file, 5798c2ecf20Sopenharmony_ci struct kobject *kobj, struct bin_attribute *attr, char *buf, 5808c2ecf20Sopenharmony_ci loff_t off, size_t count) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci struct fsi_slave *slave = to_fsi_slave(kobj_to_dev(kobj)); 5838c2ecf20Sopenharmony_ci size_t total_len, read_len; 5848c2ecf20Sopenharmony_ci int rc; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci if (off < 0) 5878c2ecf20Sopenharmony_ci return -EINVAL; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci if (off > 0xffffffff || count > 0xffffffff || off + count > 0xffffffff) 5908c2ecf20Sopenharmony_ci return -EINVAL; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci for (total_len = 0; total_len < count; total_len += read_len) { 5938c2ecf20Sopenharmony_ci read_len = aligned_access_size(off, count - total_len); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci rc = fsi_slave_read(slave, off, buf + total_len, read_len); 5968c2ecf20Sopenharmony_ci if (rc) 5978c2ecf20Sopenharmony_ci return rc; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci off += read_len; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci return count; 6038c2ecf20Sopenharmony_ci} 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_cistatic ssize_t fsi_slave_sysfs_raw_write(struct file *file, 6068c2ecf20Sopenharmony_ci struct kobject *kobj, struct bin_attribute *attr, 6078c2ecf20Sopenharmony_ci char *buf, loff_t off, size_t count) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci struct fsi_slave *slave = to_fsi_slave(kobj_to_dev(kobj)); 6108c2ecf20Sopenharmony_ci size_t total_len, write_len; 6118c2ecf20Sopenharmony_ci int rc; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci if (off < 0) 6148c2ecf20Sopenharmony_ci return -EINVAL; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci if (off > 0xffffffff || count > 0xffffffff || off + count > 0xffffffff) 6178c2ecf20Sopenharmony_ci return -EINVAL; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci for (total_len = 0; total_len < count; total_len += write_len) { 6208c2ecf20Sopenharmony_ci write_len = aligned_access_size(off, count - total_len); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci rc = fsi_slave_write(slave, off, buf + total_len, write_len); 6238c2ecf20Sopenharmony_ci if (rc) 6248c2ecf20Sopenharmony_ci return rc; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci off += write_len; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return count; 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic const struct bin_attribute fsi_slave_raw_attr = { 6338c2ecf20Sopenharmony_ci .attr = { 6348c2ecf20Sopenharmony_ci .name = "raw", 6358c2ecf20Sopenharmony_ci .mode = 0600, 6368c2ecf20Sopenharmony_ci }, 6378c2ecf20Sopenharmony_ci .size = 0, 6388c2ecf20Sopenharmony_ci .read = fsi_slave_sysfs_raw_read, 6398c2ecf20Sopenharmony_ci .write = fsi_slave_sysfs_raw_write, 6408c2ecf20Sopenharmony_ci}; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cistatic void fsi_slave_release(struct device *dev) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci struct fsi_slave *slave = to_fsi_slave(dev); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci fsi_free_minor(slave->dev.devt); 6478c2ecf20Sopenharmony_ci of_node_put(dev->of_node); 6488c2ecf20Sopenharmony_ci kfree(slave); 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic bool fsi_slave_node_matches(struct device_node *np, 6528c2ecf20Sopenharmony_ci int link, uint8_t id) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci unsigned int len, na, ns; 6558c2ecf20Sopenharmony_ci const __be32 *prop; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci na = of_n_addr_cells(np); 6588c2ecf20Sopenharmony_ci ns = of_n_size_cells(np); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci /* Ensure we have the correct format for addresses and sizes in 6618c2ecf20Sopenharmony_ci * reg properties 6628c2ecf20Sopenharmony_ci */ 6638c2ecf20Sopenharmony_ci if (na != 2 || ns != 0) 6648c2ecf20Sopenharmony_ci return false; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci prop = of_get_property(np, "reg", &len); 6678c2ecf20Sopenharmony_ci if (!prop || len != 8) 6688c2ecf20Sopenharmony_ci return false; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci return (of_read_number(prop, 1) == link) && 6718c2ecf20Sopenharmony_ci (of_read_number(prop + 1, 1) == id); 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci/* Find a matching node for the slave at (link, id). Returns NULL if none 6758c2ecf20Sopenharmony_ci * found, or a matching node with refcount already incremented. 6768c2ecf20Sopenharmony_ci */ 6778c2ecf20Sopenharmony_cistatic struct device_node *fsi_slave_find_of_node(struct fsi_master *master, 6788c2ecf20Sopenharmony_ci int link, uint8_t id) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci struct device_node *parent, *np; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci parent = dev_of_node(&master->dev); 6838c2ecf20Sopenharmony_ci if (!parent) 6848c2ecf20Sopenharmony_ci return NULL; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci for_each_child_of_node(parent, np) { 6878c2ecf20Sopenharmony_ci if (fsi_slave_node_matches(np, link, id)) 6888c2ecf20Sopenharmony_ci return np; 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci return NULL; 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic ssize_t cfam_read(struct file *filep, char __user *buf, size_t count, 6958c2ecf20Sopenharmony_ci loff_t *offset) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci struct fsi_slave *slave = filep->private_data; 6988c2ecf20Sopenharmony_ci size_t total_len, read_len; 6998c2ecf20Sopenharmony_ci loff_t off = *offset; 7008c2ecf20Sopenharmony_ci ssize_t rc; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci if (off < 0) 7038c2ecf20Sopenharmony_ci return -EINVAL; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci if (off > 0xffffffff || count > 0xffffffff || off + count > 0xffffffff) 7068c2ecf20Sopenharmony_ci return -EINVAL; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci for (total_len = 0; total_len < count; total_len += read_len) { 7098c2ecf20Sopenharmony_ci __be32 data; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci read_len = min_t(size_t, count, 4); 7128c2ecf20Sopenharmony_ci read_len -= off & 0x3; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci rc = fsi_slave_read(slave, off, &data, read_len); 7158c2ecf20Sopenharmony_ci if (rc) 7168c2ecf20Sopenharmony_ci goto fail; 7178c2ecf20Sopenharmony_ci rc = copy_to_user(buf + total_len, &data, read_len); 7188c2ecf20Sopenharmony_ci if (rc) { 7198c2ecf20Sopenharmony_ci rc = -EFAULT; 7208c2ecf20Sopenharmony_ci goto fail; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci off += read_len; 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci rc = count; 7258c2ecf20Sopenharmony_ci fail: 7268c2ecf20Sopenharmony_ci *offset = off; 7278c2ecf20Sopenharmony_ci return rc; 7288c2ecf20Sopenharmony_ci} 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_cistatic ssize_t cfam_write(struct file *filep, const char __user *buf, 7318c2ecf20Sopenharmony_ci size_t count, loff_t *offset) 7328c2ecf20Sopenharmony_ci{ 7338c2ecf20Sopenharmony_ci struct fsi_slave *slave = filep->private_data; 7348c2ecf20Sopenharmony_ci size_t total_len, write_len; 7358c2ecf20Sopenharmony_ci loff_t off = *offset; 7368c2ecf20Sopenharmony_ci ssize_t rc; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci if (off < 0) 7408c2ecf20Sopenharmony_ci return -EINVAL; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci if (off > 0xffffffff || count > 0xffffffff || off + count > 0xffffffff) 7438c2ecf20Sopenharmony_ci return -EINVAL; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci for (total_len = 0; total_len < count; total_len += write_len) { 7468c2ecf20Sopenharmony_ci __be32 data; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci write_len = min_t(size_t, count, 4); 7498c2ecf20Sopenharmony_ci write_len -= off & 0x3; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci rc = copy_from_user(&data, buf + total_len, write_len); 7528c2ecf20Sopenharmony_ci if (rc) { 7538c2ecf20Sopenharmony_ci rc = -EFAULT; 7548c2ecf20Sopenharmony_ci goto fail; 7558c2ecf20Sopenharmony_ci } 7568c2ecf20Sopenharmony_ci rc = fsi_slave_write(slave, off, &data, write_len); 7578c2ecf20Sopenharmony_ci if (rc) 7588c2ecf20Sopenharmony_ci goto fail; 7598c2ecf20Sopenharmony_ci off += write_len; 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci rc = count; 7628c2ecf20Sopenharmony_ci fail: 7638c2ecf20Sopenharmony_ci *offset = off; 7648c2ecf20Sopenharmony_ci return rc; 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_cistatic loff_t cfam_llseek(struct file *file, loff_t offset, int whence) 7688c2ecf20Sopenharmony_ci{ 7698c2ecf20Sopenharmony_ci switch (whence) { 7708c2ecf20Sopenharmony_ci case SEEK_CUR: 7718c2ecf20Sopenharmony_ci break; 7728c2ecf20Sopenharmony_ci case SEEK_SET: 7738c2ecf20Sopenharmony_ci file->f_pos = offset; 7748c2ecf20Sopenharmony_ci break; 7758c2ecf20Sopenharmony_ci default: 7768c2ecf20Sopenharmony_ci return -EINVAL; 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci return offset; 7808c2ecf20Sopenharmony_ci} 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_cistatic int cfam_open(struct inode *inode, struct file *file) 7838c2ecf20Sopenharmony_ci{ 7848c2ecf20Sopenharmony_ci struct fsi_slave *slave = container_of(inode->i_cdev, struct fsi_slave, cdev); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci file->private_data = slave; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci return 0; 7898c2ecf20Sopenharmony_ci} 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_cistatic const struct file_operations cfam_fops = { 7928c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 7938c2ecf20Sopenharmony_ci .open = cfam_open, 7948c2ecf20Sopenharmony_ci .llseek = cfam_llseek, 7958c2ecf20Sopenharmony_ci .read = cfam_read, 7968c2ecf20Sopenharmony_ci .write = cfam_write, 7978c2ecf20Sopenharmony_ci}; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_cistatic ssize_t send_term_store(struct device *dev, 8008c2ecf20Sopenharmony_ci struct device_attribute *attr, 8018c2ecf20Sopenharmony_ci const char *buf, size_t count) 8028c2ecf20Sopenharmony_ci{ 8038c2ecf20Sopenharmony_ci struct fsi_slave *slave = to_fsi_slave(dev); 8048c2ecf20Sopenharmony_ci struct fsi_master *master = slave->master; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci if (!master->term) 8078c2ecf20Sopenharmony_ci return -ENODEV; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci master->term(master, slave->link, slave->id); 8108c2ecf20Sopenharmony_ci return count; 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(send_term); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_cistatic ssize_t slave_send_echo_show(struct device *dev, 8168c2ecf20Sopenharmony_ci struct device_attribute *attr, 8178c2ecf20Sopenharmony_ci char *buf) 8188c2ecf20Sopenharmony_ci{ 8198c2ecf20Sopenharmony_ci struct fsi_slave *slave = to_fsi_slave(dev); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", slave->t_send_delay); 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_cistatic ssize_t slave_send_echo_store(struct device *dev, 8258c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci struct fsi_slave *slave = to_fsi_slave(dev); 8288c2ecf20Sopenharmony_ci struct fsi_master *master = slave->master; 8298c2ecf20Sopenharmony_ci unsigned long val; 8308c2ecf20Sopenharmony_ci int rc; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci if (kstrtoul(buf, 0, &val) < 0) 8338c2ecf20Sopenharmony_ci return -EINVAL; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci if (val < 1 || val > 16) 8368c2ecf20Sopenharmony_ci return -EINVAL; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci if (!master->link_config) 8398c2ecf20Sopenharmony_ci return -ENXIO; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci /* Current HW mandates that send and echo delay are identical */ 8428c2ecf20Sopenharmony_ci slave->t_send_delay = val; 8438c2ecf20Sopenharmony_ci slave->t_echo_delay = val; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci rc = fsi_slave_set_smode(slave); 8468c2ecf20Sopenharmony_ci if (rc < 0) 8478c2ecf20Sopenharmony_ci return rc; 8488c2ecf20Sopenharmony_ci if (master->link_config) 8498c2ecf20Sopenharmony_ci master->link_config(master, slave->link, 8508c2ecf20Sopenharmony_ci slave->t_send_delay, 8518c2ecf20Sopenharmony_ci slave->t_echo_delay); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci return count; 8548c2ecf20Sopenharmony_ci} 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_cistatic DEVICE_ATTR(send_echo_delays, 0600, 8578c2ecf20Sopenharmony_ci slave_send_echo_show, slave_send_echo_store); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_cistatic ssize_t chip_id_show(struct device *dev, 8608c2ecf20Sopenharmony_ci struct device_attribute *attr, 8618c2ecf20Sopenharmony_ci char *buf) 8628c2ecf20Sopenharmony_ci{ 8638c2ecf20Sopenharmony_ci struct fsi_slave *slave = to_fsi_slave(dev); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", slave->chip_id); 8668c2ecf20Sopenharmony_ci} 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(chip_id); 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_cistatic ssize_t cfam_id_show(struct device *dev, 8718c2ecf20Sopenharmony_ci struct device_attribute *attr, 8728c2ecf20Sopenharmony_ci char *buf) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci struct fsi_slave *slave = to_fsi_slave(dev); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci return sprintf(buf, "0x%x\n", slave->cfam_id); 8778c2ecf20Sopenharmony_ci} 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(cfam_id); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_cistatic struct attribute *cfam_attr[] = { 8828c2ecf20Sopenharmony_ci &dev_attr_send_echo_delays.attr, 8838c2ecf20Sopenharmony_ci &dev_attr_chip_id.attr, 8848c2ecf20Sopenharmony_ci &dev_attr_cfam_id.attr, 8858c2ecf20Sopenharmony_ci &dev_attr_send_term.attr, 8868c2ecf20Sopenharmony_ci NULL, 8878c2ecf20Sopenharmony_ci}; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_cistatic const struct attribute_group cfam_attr_group = { 8908c2ecf20Sopenharmony_ci .attrs = cfam_attr, 8918c2ecf20Sopenharmony_ci}; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_cistatic const struct attribute_group *cfam_attr_groups[] = { 8948c2ecf20Sopenharmony_ci &cfam_attr_group, 8958c2ecf20Sopenharmony_ci NULL, 8968c2ecf20Sopenharmony_ci}; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_cistatic char *cfam_devnode(struct device *dev, umode_t *mode, 8998c2ecf20Sopenharmony_ci kuid_t *uid, kgid_t *gid) 9008c2ecf20Sopenharmony_ci{ 9018c2ecf20Sopenharmony_ci struct fsi_slave *slave = to_fsi_slave(dev); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci#ifdef CONFIG_FSI_NEW_DEV_NODE 9048c2ecf20Sopenharmony_ci return kasprintf(GFP_KERNEL, "fsi/cfam%d", slave->cdev_idx); 9058c2ecf20Sopenharmony_ci#else 9068c2ecf20Sopenharmony_ci return kasprintf(GFP_KERNEL, "cfam%d", slave->cdev_idx); 9078c2ecf20Sopenharmony_ci#endif 9088c2ecf20Sopenharmony_ci} 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_cistatic const struct device_type cfam_type = { 9118c2ecf20Sopenharmony_ci .name = "cfam", 9128c2ecf20Sopenharmony_ci .devnode = cfam_devnode, 9138c2ecf20Sopenharmony_ci .groups = cfam_attr_groups 9148c2ecf20Sopenharmony_ci}; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_cistatic char *fsi_cdev_devnode(struct device *dev, umode_t *mode, 9178c2ecf20Sopenharmony_ci kuid_t *uid, kgid_t *gid) 9188c2ecf20Sopenharmony_ci{ 9198c2ecf20Sopenharmony_ci#ifdef CONFIG_FSI_NEW_DEV_NODE 9208c2ecf20Sopenharmony_ci return kasprintf(GFP_KERNEL, "fsi/%s", dev_name(dev)); 9218c2ecf20Sopenharmony_ci#else 9228c2ecf20Sopenharmony_ci return kasprintf(GFP_KERNEL, "%s", dev_name(dev)); 9238c2ecf20Sopenharmony_ci#endif 9248c2ecf20Sopenharmony_ci} 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ciconst struct device_type fsi_cdev_type = { 9278c2ecf20Sopenharmony_ci .name = "fsi-cdev", 9288c2ecf20Sopenharmony_ci .devnode = fsi_cdev_devnode, 9298c2ecf20Sopenharmony_ci}; 9308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_cdev_type); 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci/* Backward compatible /dev/ numbering in "old style" mode */ 9338c2ecf20Sopenharmony_cistatic int fsi_adjust_index(int index) 9348c2ecf20Sopenharmony_ci{ 9358c2ecf20Sopenharmony_ci#ifdef CONFIG_FSI_NEW_DEV_NODE 9368c2ecf20Sopenharmony_ci return index; 9378c2ecf20Sopenharmony_ci#else 9388c2ecf20Sopenharmony_ci return index + 1; 9398c2ecf20Sopenharmony_ci#endif 9408c2ecf20Sopenharmony_ci} 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_cistatic int __fsi_get_new_minor(struct fsi_slave *slave, enum fsi_dev_type type, 9438c2ecf20Sopenharmony_ci dev_t *out_dev, int *out_index) 9448c2ecf20Sopenharmony_ci{ 9458c2ecf20Sopenharmony_ci int cid = slave->chip_id; 9468c2ecf20Sopenharmony_ci int id; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci /* Check if we qualify for legacy numbering */ 9498c2ecf20Sopenharmony_ci if (cid >= 0 && cid < 16 && type < 4) { 9508c2ecf20Sopenharmony_ci /* Try reserving the legacy number */ 9518c2ecf20Sopenharmony_ci id = (cid << 4) | type; 9528c2ecf20Sopenharmony_ci id = ida_simple_get(&fsi_minor_ida, id, id + 1, GFP_KERNEL); 9538c2ecf20Sopenharmony_ci if (id >= 0) { 9548c2ecf20Sopenharmony_ci *out_index = fsi_adjust_index(cid); 9558c2ecf20Sopenharmony_ci *out_dev = fsi_base_dev + id; 9568c2ecf20Sopenharmony_ci return 0; 9578c2ecf20Sopenharmony_ci } 9588c2ecf20Sopenharmony_ci /* Other failure */ 9598c2ecf20Sopenharmony_ci if (id != -ENOSPC) 9608c2ecf20Sopenharmony_ci return id; 9618c2ecf20Sopenharmony_ci /* Fallback to non-legacy allocation */ 9628c2ecf20Sopenharmony_ci } 9638c2ecf20Sopenharmony_ci id = ida_simple_get(&fsi_minor_ida, FSI_CHAR_LEGACY_TOP, 9648c2ecf20Sopenharmony_ci FSI_CHAR_MAX_DEVICES, GFP_KERNEL); 9658c2ecf20Sopenharmony_ci if (id < 0) 9668c2ecf20Sopenharmony_ci return id; 9678c2ecf20Sopenharmony_ci *out_index = fsi_adjust_index(id); 9688c2ecf20Sopenharmony_ci *out_dev = fsi_base_dev + id; 9698c2ecf20Sopenharmony_ci return 0; 9708c2ecf20Sopenharmony_ci} 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ciint fsi_get_new_minor(struct fsi_device *fdev, enum fsi_dev_type type, 9738c2ecf20Sopenharmony_ci dev_t *out_dev, int *out_index) 9748c2ecf20Sopenharmony_ci{ 9758c2ecf20Sopenharmony_ci return __fsi_get_new_minor(fdev->slave, type, out_dev, out_index); 9768c2ecf20Sopenharmony_ci} 9778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_get_new_minor); 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_civoid fsi_free_minor(dev_t dev) 9808c2ecf20Sopenharmony_ci{ 9818c2ecf20Sopenharmony_ci ida_simple_remove(&fsi_minor_ida, MINOR(dev)); 9828c2ecf20Sopenharmony_ci} 9838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_free_minor); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_cistatic int fsi_slave_init(struct fsi_master *master, int link, uint8_t id) 9868c2ecf20Sopenharmony_ci{ 9878c2ecf20Sopenharmony_ci uint32_t cfam_id; 9888c2ecf20Sopenharmony_ci struct fsi_slave *slave; 9898c2ecf20Sopenharmony_ci uint8_t crc; 9908c2ecf20Sopenharmony_ci __be32 data, llmode, slbus; 9918c2ecf20Sopenharmony_ci int rc; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci /* Currently, we only support single slaves on a link, and use the 9948c2ecf20Sopenharmony_ci * full 23-bit address range 9958c2ecf20Sopenharmony_ci */ 9968c2ecf20Sopenharmony_ci if (id != 0) 9978c2ecf20Sopenharmony_ci return -EINVAL; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci rc = fsi_master_read(master, link, id, 0, &data, sizeof(data)); 10008c2ecf20Sopenharmony_ci if (rc) { 10018c2ecf20Sopenharmony_ci dev_dbg(&master->dev, "can't read slave %02x:%02x %d\n", 10028c2ecf20Sopenharmony_ci link, id, rc); 10038c2ecf20Sopenharmony_ci return -ENODEV; 10048c2ecf20Sopenharmony_ci } 10058c2ecf20Sopenharmony_ci cfam_id = be32_to_cpu(data); 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci crc = crc4(0, cfam_id, 32); 10088c2ecf20Sopenharmony_ci if (crc) { 10098c2ecf20Sopenharmony_ci dev_warn(&master->dev, "slave %02x:%02x invalid cfam id CRC!\n", 10108c2ecf20Sopenharmony_ci link, id); 10118c2ecf20Sopenharmony_ci return -EIO; 10128c2ecf20Sopenharmony_ci } 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci dev_dbg(&master->dev, "fsi: found chip %08x at %02x:%02x:%02x\n", 10158c2ecf20Sopenharmony_ci cfam_id, master->idx, link, id); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci /* If we're behind a master that doesn't provide a self-running bus 10188c2ecf20Sopenharmony_ci * clock, put the slave into async mode 10198c2ecf20Sopenharmony_ci */ 10208c2ecf20Sopenharmony_ci if (master->flags & FSI_MASTER_FLAG_SWCLOCK) { 10218c2ecf20Sopenharmony_ci llmode = cpu_to_be32(FSI_LLMODE_ASYNC); 10228c2ecf20Sopenharmony_ci rc = fsi_master_write(master, link, id, 10238c2ecf20Sopenharmony_ci FSI_SLAVE_BASE + FSI_LLMODE, 10248c2ecf20Sopenharmony_ci &llmode, sizeof(llmode)); 10258c2ecf20Sopenharmony_ci if (rc) 10268c2ecf20Sopenharmony_ci dev_warn(&master->dev, 10278c2ecf20Sopenharmony_ci "can't set llmode on slave:%02x:%02x %d\n", 10288c2ecf20Sopenharmony_ci link, id, rc); 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci /* We can communicate with a slave; create the slave device and 10328c2ecf20Sopenharmony_ci * register. 10338c2ecf20Sopenharmony_ci */ 10348c2ecf20Sopenharmony_ci slave = kzalloc(sizeof(*slave), GFP_KERNEL); 10358c2ecf20Sopenharmony_ci if (!slave) 10368c2ecf20Sopenharmony_ci return -ENOMEM; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci dev_set_name(&slave->dev, "slave@%02x:%02x", link, id); 10398c2ecf20Sopenharmony_ci slave->dev.type = &cfam_type; 10408c2ecf20Sopenharmony_ci slave->dev.parent = &master->dev; 10418c2ecf20Sopenharmony_ci slave->dev.of_node = fsi_slave_find_of_node(master, link, id); 10428c2ecf20Sopenharmony_ci slave->dev.release = fsi_slave_release; 10438c2ecf20Sopenharmony_ci device_initialize(&slave->dev); 10448c2ecf20Sopenharmony_ci slave->cfam_id = cfam_id; 10458c2ecf20Sopenharmony_ci slave->master = master; 10468c2ecf20Sopenharmony_ci slave->link = link; 10478c2ecf20Sopenharmony_ci slave->id = id; 10488c2ecf20Sopenharmony_ci slave->size = FSI_SLAVE_SIZE_23b; 10498c2ecf20Sopenharmony_ci slave->t_send_delay = 16; 10508c2ecf20Sopenharmony_ci slave->t_echo_delay = 16; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci /* Get chip ID if any */ 10538c2ecf20Sopenharmony_ci slave->chip_id = -1; 10548c2ecf20Sopenharmony_ci if (slave->dev.of_node) { 10558c2ecf20Sopenharmony_ci uint32_t prop; 10568c2ecf20Sopenharmony_ci if (!of_property_read_u32(slave->dev.of_node, "chip-id", &prop)) 10578c2ecf20Sopenharmony_ci slave->chip_id = prop; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci slbus = cpu_to_be32(FSI_SLBUS_FORCE); 10628c2ecf20Sopenharmony_ci rc = fsi_master_write(master, link, id, FSI_SLAVE_BASE + FSI_SLBUS, 10638c2ecf20Sopenharmony_ci &slbus, sizeof(slbus)); 10648c2ecf20Sopenharmony_ci if (rc) 10658c2ecf20Sopenharmony_ci dev_warn(&master->dev, 10668c2ecf20Sopenharmony_ci "can't set slbus on slave:%02x:%02x %d\n", link, id, 10678c2ecf20Sopenharmony_ci rc); 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci rc = fsi_slave_set_smode(slave); 10708c2ecf20Sopenharmony_ci if (rc) { 10718c2ecf20Sopenharmony_ci dev_warn(&master->dev, 10728c2ecf20Sopenharmony_ci "can't set smode on slave:%02x:%02x %d\n", 10738c2ecf20Sopenharmony_ci link, id, rc); 10748c2ecf20Sopenharmony_ci goto err_free; 10758c2ecf20Sopenharmony_ci } 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci /* Allocate a minor in the FSI space */ 10788c2ecf20Sopenharmony_ci rc = __fsi_get_new_minor(slave, fsi_dev_cfam, &slave->dev.devt, 10798c2ecf20Sopenharmony_ci &slave->cdev_idx); 10808c2ecf20Sopenharmony_ci if (rc) 10818c2ecf20Sopenharmony_ci goto err_free; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci /* Create chardev for userspace access */ 10848c2ecf20Sopenharmony_ci cdev_init(&slave->cdev, &cfam_fops); 10858c2ecf20Sopenharmony_ci rc = cdev_device_add(&slave->cdev, &slave->dev); 10868c2ecf20Sopenharmony_ci if (rc) { 10878c2ecf20Sopenharmony_ci dev_err(&slave->dev, "Error %d creating slave device\n", rc); 10888c2ecf20Sopenharmony_ci goto err_free_ida; 10898c2ecf20Sopenharmony_ci } 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci /* Now that we have the cdev registered with the core, any fatal 10928c2ecf20Sopenharmony_ci * failures beyond this point will need to clean up through 10938c2ecf20Sopenharmony_ci * cdev_device_del(). Fortunately though, nothing past here is fatal. 10948c2ecf20Sopenharmony_ci */ 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci if (master->link_config) 10978c2ecf20Sopenharmony_ci master->link_config(master, link, 10988c2ecf20Sopenharmony_ci slave->t_send_delay, 10998c2ecf20Sopenharmony_ci slave->t_echo_delay); 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci /* Legacy raw file -> to be removed */ 11028c2ecf20Sopenharmony_ci rc = device_create_bin_file(&slave->dev, &fsi_slave_raw_attr); 11038c2ecf20Sopenharmony_ci if (rc) 11048c2ecf20Sopenharmony_ci dev_warn(&slave->dev, "failed to create raw attr: %d\n", rc); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci rc = fsi_slave_scan(slave); 11088c2ecf20Sopenharmony_ci if (rc) 11098c2ecf20Sopenharmony_ci dev_dbg(&master->dev, "failed during slave scan with: %d\n", 11108c2ecf20Sopenharmony_ci rc); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci return 0; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_cierr_free_ida: 11158c2ecf20Sopenharmony_ci fsi_free_minor(slave->dev.devt); 11168c2ecf20Sopenharmony_cierr_free: 11178c2ecf20Sopenharmony_ci of_node_put(slave->dev.of_node); 11188c2ecf20Sopenharmony_ci kfree(slave); 11198c2ecf20Sopenharmony_ci return rc; 11208c2ecf20Sopenharmony_ci} 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci/* FSI master support */ 11238c2ecf20Sopenharmony_cistatic int fsi_check_access(uint32_t addr, size_t size) 11248c2ecf20Sopenharmony_ci{ 11258c2ecf20Sopenharmony_ci if (size == 4) { 11268c2ecf20Sopenharmony_ci if (addr & 0x3) 11278c2ecf20Sopenharmony_ci return -EINVAL; 11288c2ecf20Sopenharmony_ci } else if (size == 2) { 11298c2ecf20Sopenharmony_ci if (addr & 0x1) 11308c2ecf20Sopenharmony_ci return -EINVAL; 11318c2ecf20Sopenharmony_ci } else if (size != 1) 11328c2ecf20Sopenharmony_ci return -EINVAL; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci return 0; 11358c2ecf20Sopenharmony_ci} 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_cistatic int fsi_master_read(struct fsi_master *master, int link, 11388c2ecf20Sopenharmony_ci uint8_t slave_id, uint32_t addr, void *val, size_t size) 11398c2ecf20Sopenharmony_ci{ 11408c2ecf20Sopenharmony_ci int rc; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci trace_fsi_master_read(master, link, slave_id, addr, size); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci rc = fsi_check_access(addr, size); 11458c2ecf20Sopenharmony_ci if (!rc) 11468c2ecf20Sopenharmony_ci rc = master->read(master, link, slave_id, addr, val, size); 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci trace_fsi_master_rw_result(master, link, slave_id, addr, size, 11498c2ecf20Sopenharmony_ci false, val, rc); 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci return rc; 11528c2ecf20Sopenharmony_ci} 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_cistatic int fsi_master_write(struct fsi_master *master, int link, 11558c2ecf20Sopenharmony_ci uint8_t slave_id, uint32_t addr, const void *val, size_t size) 11568c2ecf20Sopenharmony_ci{ 11578c2ecf20Sopenharmony_ci int rc; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci trace_fsi_master_write(master, link, slave_id, addr, size, val); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci rc = fsi_check_access(addr, size); 11628c2ecf20Sopenharmony_ci if (!rc) 11638c2ecf20Sopenharmony_ci rc = master->write(master, link, slave_id, addr, val, size); 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci trace_fsi_master_rw_result(master, link, slave_id, addr, size, 11668c2ecf20Sopenharmony_ci true, val, rc); 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci return rc; 11698c2ecf20Sopenharmony_ci} 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_cistatic int fsi_master_link_disable(struct fsi_master *master, int link) 11728c2ecf20Sopenharmony_ci{ 11738c2ecf20Sopenharmony_ci if (master->link_enable) 11748c2ecf20Sopenharmony_ci return master->link_enable(master, link, false); 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci return 0; 11778c2ecf20Sopenharmony_ci} 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_cistatic int fsi_master_link_enable(struct fsi_master *master, int link) 11808c2ecf20Sopenharmony_ci{ 11818c2ecf20Sopenharmony_ci if (master->link_enable) 11828c2ecf20Sopenharmony_ci return master->link_enable(master, link, true); 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci return 0; 11858c2ecf20Sopenharmony_ci} 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci/* 11888c2ecf20Sopenharmony_ci * Issue a break command on this link 11898c2ecf20Sopenharmony_ci */ 11908c2ecf20Sopenharmony_cistatic int fsi_master_break(struct fsi_master *master, int link) 11918c2ecf20Sopenharmony_ci{ 11928c2ecf20Sopenharmony_ci int rc = 0; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci trace_fsi_master_break(master, link); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci if (master->send_break) 11978c2ecf20Sopenharmony_ci rc = master->send_break(master, link); 11988c2ecf20Sopenharmony_ci if (master->link_config) 11998c2ecf20Sopenharmony_ci master->link_config(master, link, 16, 16); 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci return rc; 12028c2ecf20Sopenharmony_ci} 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_cistatic int fsi_master_scan(struct fsi_master *master) 12058c2ecf20Sopenharmony_ci{ 12068c2ecf20Sopenharmony_ci int link, rc; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci for (link = 0; link < master->n_links; link++) { 12098c2ecf20Sopenharmony_ci rc = fsi_master_link_enable(master, link); 12108c2ecf20Sopenharmony_ci if (rc) { 12118c2ecf20Sopenharmony_ci dev_dbg(&master->dev, 12128c2ecf20Sopenharmony_ci "enable link %d failed: %d\n", link, rc); 12138c2ecf20Sopenharmony_ci continue; 12148c2ecf20Sopenharmony_ci } 12158c2ecf20Sopenharmony_ci rc = fsi_master_break(master, link); 12168c2ecf20Sopenharmony_ci if (rc) { 12178c2ecf20Sopenharmony_ci fsi_master_link_disable(master, link); 12188c2ecf20Sopenharmony_ci dev_dbg(&master->dev, 12198c2ecf20Sopenharmony_ci "break to link %d failed: %d\n", link, rc); 12208c2ecf20Sopenharmony_ci continue; 12218c2ecf20Sopenharmony_ci } 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci rc = fsi_slave_init(master, link, 0); 12248c2ecf20Sopenharmony_ci if (rc) 12258c2ecf20Sopenharmony_ci fsi_master_link_disable(master, link); 12268c2ecf20Sopenharmony_ci } 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci return 0; 12298c2ecf20Sopenharmony_ci} 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_cistatic int fsi_slave_remove_device(struct device *dev, void *arg) 12328c2ecf20Sopenharmony_ci{ 12338c2ecf20Sopenharmony_ci device_unregister(dev); 12348c2ecf20Sopenharmony_ci return 0; 12358c2ecf20Sopenharmony_ci} 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_cistatic int fsi_master_remove_slave(struct device *dev, void *arg) 12388c2ecf20Sopenharmony_ci{ 12398c2ecf20Sopenharmony_ci struct fsi_slave *slave = to_fsi_slave(dev); 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci device_for_each_child(dev, NULL, fsi_slave_remove_device); 12428c2ecf20Sopenharmony_ci cdev_device_del(&slave->cdev, &slave->dev); 12438c2ecf20Sopenharmony_ci put_device(dev); 12448c2ecf20Sopenharmony_ci return 0; 12458c2ecf20Sopenharmony_ci} 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_cistatic void fsi_master_unscan(struct fsi_master *master) 12488c2ecf20Sopenharmony_ci{ 12498c2ecf20Sopenharmony_ci device_for_each_child(&master->dev, NULL, fsi_master_remove_slave); 12508c2ecf20Sopenharmony_ci} 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ciint fsi_master_rescan(struct fsi_master *master) 12538c2ecf20Sopenharmony_ci{ 12548c2ecf20Sopenharmony_ci int rc; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci mutex_lock(&master->scan_lock); 12578c2ecf20Sopenharmony_ci fsi_master_unscan(master); 12588c2ecf20Sopenharmony_ci rc = fsi_master_scan(master); 12598c2ecf20Sopenharmony_ci mutex_unlock(&master->scan_lock); 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci return rc; 12628c2ecf20Sopenharmony_ci} 12638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_master_rescan); 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_cistatic ssize_t master_rescan_store(struct device *dev, 12668c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 12678c2ecf20Sopenharmony_ci{ 12688c2ecf20Sopenharmony_ci struct fsi_master *master = to_fsi_master(dev); 12698c2ecf20Sopenharmony_ci int rc; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci rc = fsi_master_rescan(master); 12728c2ecf20Sopenharmony_ci if (rc < 0) 12738c2ecf20Sopenharmony_ci return rc; 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci return count; 12768c2ecf20Sopenharmony_ci} 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_cistatic DEVICE_ATTR(rescan, 0200, NULL, master_rescan_store); 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_cistatic ssize_t master_break_store(struct device *dev, 12818c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 12828c2ecf20Sopenharmony_ci{ 12838c2ecf20Sopenharmony_ci struct fsi_master *master = to_fsi_master(dev); 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci fsi_master_break(master, 0); 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci return count; 12888c2ecf20Sopenharmony_ci} 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_cistatic DEVICE_ATTR(break, 0200, NULL, master_break_store); 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_cistatic struct attribute *master_attrs[] = { 12938c2ecf20Sopenharmony_ci &dev_attr_break.attr, 12948c2ecf20Sopenharmony_ci &dev_attr_rescan.attr, 12958c2ecf20Sopenharmony_ci NULL 12968c2ecf20Sopenharmony_ci}; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(master); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_cistatic struct class fsi_master_class = { 13018c2ecf20Sopenharmony_ci .name = "fsi-master", 13028c2ecf20Sopenharmony_ci .dev_groups = master_groups, 13038c2ecf20Sopenharmony_ci}; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ciint fsi_master_register(struct fsi_master *master) 13068c2ecf20Sopenharmony_ci{ 13078c2ecf20Sopenharmony_ci int rc; 13088c2ecf20Sopenharmony_ci struct device_node *np; 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci mutex_init(&master->scan_lock); 13118c2ecf20Sopenharmony_ci master->idx = ida_simple_get(&master_ida, 0, INT_MAX, GFP_KERNEL); 13128c2ecf20Sopenharmony_ci if (master->idx < 0) 13138c2ecf20Sopenharmony_ci return master->idx; 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci dev_set_name(&master->dev, "fsi%d", master->idx); 13168c2ecf20Sopenharmony_ci master->dev.class = &fsi_master_class; 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci rc = device_register(&master->dev); 13198c2ecf20Sopenharmony_ci if (rc) { 13208c2ecf20Sopenharmony_ci ida_simple_remove(&master_ida, master->idx); 13218c2ecf20Sopenharmony_ci return rc; 13228c2ecf20Sopenharmony_ci } 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci np = dev_of_node(&master->dev); 13258c2ecf20Sopenharmony_ci if (!of_property_read_bool(np, "no-scan-on-init")) { 13268c2ecf20Sopenharmony_ci mutex_lock(&master->scan_lock); 13278c2ecf20Sopenharmony_ci fsi_master_scan(master); 13288c2ecf20Sopenharmony_ci mutex_unlock(&master->scan_lock); 13298c2ecf20Sopenharmony_ci } 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci return 0; 13328c2ecf20Sopenharmony_ci} 13338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_master_register); 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_civoid fsi_master_unregister(struct fsi_master *master) 13368c2ecf20Sopenharmony_ci{ 13378c2ecf20Sopenharmony_ci if (master->idx >= 0) { 13388c2ecf20Sopenharmony_ci ida_simple_remove(&master_ida, master->idx); 13398c2ecf20Sopenharmony_ci master->idx = -1; 13408c2ecf20Sopenharmony_ci } 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci mutex_lock(&master->scan_lock); 13438c2ecf20Sopenharmony_ci fsi_master_unscan(master); 13448c2ecf20Sopenharmony_ci mutex_unlock(&master->scan_lock); 13458c2ecf20Sopenharmony_ci device_unregister(&master->dev); 13468c2ecf20Sopenharmony_ci} 13478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_master_unregister); 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci/* FSI core & Linux bus type definitions */ 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_cistatic int fsi_bus_match(struct device *dev, struct device_driver *drv) 13528c2ecf20Sopenharmony_ci{ 13538c2ecf20Sopenharmony_ci struct fsi_device *fsi_dev = to_fsi_dev(dev); 13548c2ecf20Sopenharmony_ci struct fsi_driver *fsi_drv = to_fsi_drv(drv); 13558c2ecf20Sopenharmony_ci const struct fsi_device_id *id; 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci if (!fsi_drv->id_table) 13588c2ecf20Sopenharmony_ci return 0; 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci for (id = fsi_drv->id_table; id->engine_type; id++) { 13618c2ecf20Sopenharmony_ci if (id->engine_type != fsi_dev->engine_type) 13628c2ecf20Sopenharmony_ci continue; 13638c2ecf20Sopenharmony_ci if (id->version == FSI_VERSION_ANY || 13648c2ecf20Sopenharmony_ci id->version == fsi_dev->version) 13658c2ecf20Sopenharmony_ci return 1; 13668c2ecf20Sopenharmony_ci } 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci return 0; 13698c2ecf20Sopenharmony_ci} 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ciint fsi_driver_register(struct fsi_driver *fsi_drv) 13728c2ecf20Sopenharmony_ci{ 13738c2ecf20Sopenharmony_ci if (!fsi_drv) 13748c2ecf20Sopenharmony_ci return -EINVAL; 13758c2ecf20Sopenharmony_ci if (!fsi_drv->id_table) 13768c2ecf20Sopenharmony_ci return -EINVAL; 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci return driver_register(&fsi_drv->drv); 13798c2ecf20Sopenharmony_ci} 13808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_driver_register); 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_civoid fsi_driver_unregister(struct fsi_driver *fsi_drv) 13838c2ecf20Sopenharmony_ci{ 13848c2ecf20Sopenharmony_ci driver_unregister(&fsi_drv->drv); 13858c2ecf20Sopenharmony_ci} 13868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_driver_unregister); 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_cistruct bus_type fsi_bus_type = { 13898c2ecf20Sopenharmony_ci .name = "fsi", 13908c2ecf20Sopenharmony_ci .match = fsi_bus_match, 13918c2ecf20Sopenharmony_ci}; 13928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_bus_type); 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_cistatic int __init fsi_init(void) 13958c2ecf20Sopenharmony_ci{ 13968c2ecf20Sopenharmony_ci int rc; 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci rc = alloc_chrdev_region(&fsi_base_dev, 0, FSI_CHAR_MAX_DEVICES, "fsi"); 13998c2ecf20Sopenharmony_ci if (rc) 14008c2ecf20Sopenharmony_ci return rc; 14018c2ecf20Sopenharmony_ci rc = bus_register(&fsi_bus_type); 14028c2ecf20Sopenharmony_ci if (rc) 14038c2ecf20Sopenharmony_ci goto fail_bus; 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci rc = class_register(&fsi_master_class); 14068c2ecf20Sopenharmony_ci if (rc) 14078c2ecf20Sopenharmony_ci goto fail_class; 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci return 0; 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci fail_class: 14128c2ecf20Sopenharmony_ci bus_unregister(&fsi_bus_type); 14138c2ecf20Sopenharmony_ci fail_bus: 14148c2ecf20Sopenharmony_ci unregister_chrdev_region(fsi_base_dev, FSI_CHAR_MAX_DEVICES); 14158c2ecf20Sopenharmony_ci return rc; 14168c2ecf20Sopenharmony_ci} 14178c2ecf20Sopenharmony_cipostcore_initcall(fsi_init); 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_cistatic void fsi_exit(void) 14208c2ecf20Sopenharmony_ci{ 14218c2ecf20Sopenharmony_ci class_unregister(&fsi_master_class); 14228c2ecf20Sopenharmony_ci bus_unregister(&fsi_bus_type); 14238c2ecf20Sopenharmony_ci unregister_chrdev_region(fsi_base_dev, FSI_CHAR_MAX_DEVICES); 14248c2ecf20Sopenharmony_ci ida_destroy(&fsi_minor_ida); 14258c2ecf20Sopenharmony_ci} 14268c2ecf20Sopenharmony_cimodule_exit(fsi_exit); 14278c2ecf20Sopenharmony_cimodule_param(discard_errors, int, 0664); 14288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 14298c2ecf20Sopenharmony_ciMODULE_PARM_DESC(discard_errors, "Don't invoke error handling on bus accesses"); 1430