18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Industry-pack bus support functions. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2011-2012 CERN (www.cern.ch) 68c2ecf20Sopenharmony_ci * Author: Samuel Iglesias Gonsalvez <siglesias@igalia.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/idr.h> 128c2ecf20Sopenharmony_ci#include <linux/io.h> 138c2ecf20Sopenharmony_ci#include <linux/ipack.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define to_ipack_dev(device) container_of(device, struct ipack_device, dev) 168c2ecf20Sopenharmony_ci#define to_ipack_driver(drv) container_of(drv, struct ipack_driver, driver) 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic DEFINE_IDA(ipack_ida); 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic void ipack_device_release(struct device *dev) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci struct ipack_device *device = to_ipack_dev(dev); 238c2ecf20Sopenharmony_ci kfree(device->id); 248c2ecf20Sopenharmony_ci device->release(device); 258c2ecf20Sopenharmony_ci} 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic inline const struct ipack_device_id * 288c2ecf20Sopenharmony_ciipack_match_one_device(const struct ipack_device_id *id, 298c2ecf20Sopenharmony_ci const struct ipack_device *device) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci if ((id->format == IPACK_ANY_FORMAT || 328c2ecf20Sopenharmony_ci id->format == device->id_format) && 338c2ecf20Sopenharmony_ci (id->vendor == IPACK_ANY_ID || id->vendor == device->id_vendor) && 348c2ecf20Sopenharmony_ci (id->device == IPACK_ANY_ID || id->device == device->id_device)) 358c2ecf20Sopenharmony_ci return id; 368c2ecf20Sopenharmony_ci return NULL; 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic const struct ipack_device_id * 408c2ecf20Sopenharmony_ciipack_match_id(const struct ipack_device_id *ids, struct ipack_device *idev) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci if (ids) { 438c2ecf20Sopenharmony_ci while (ids->vendor || ids->device) { 448c2ecf20Sopenharmony_ci if (ipack_match_one_device(ids, idev)) 458c2ecf20Sopenharmony_ci return ids; 468c2ecf20Sopenharmony_ci ids++; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci return NULL; 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic int ipack_bus_match(struct device *dev, struct device_driver *drv) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct ipack_device *idev = to_ipack_dev(dev); 558c2ecf20Sopenharmony_ci struct ipack_driver *idrv = to_ipack_driver(drv); 568c2ecf20Sopenharmony_ci const struct ipack_device_id *found_id; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci found_id = ipack_match_id(idrv->id_table, idev); 598c2ecf20Sopenharmony_ci return found_id ? 1 : 0; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int ipack_bus_probe(struct device *device) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct ipack_device *dev = to_ipack_dev(device); 658c2ecf20Sopenharmony_ci struct ipack_driver *drv = to_ipack_driver(device->driver); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (!drv->ops->probe) 688c2ecf20Sopenharmony_ci return -EINVAL; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return drv->ops->probe(dev); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int ipack_bus_remove(struct device *device) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct ipack_device *dev = to_ipack_dev(device); 768c2ecf20Sopenharmony_ci struct ipack_driver *drv = to_ipack_driver(device->driver); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (!drv->ops->remove) 798c2ecf20Sopenharmony_ci return -EINVAL; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci drv->ops->remove(dev); 828c2ecf20Sopenharmony_ci return 0; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic int ipack_uevent(struct device *dev, struct kobj_uevent_env *env) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct ipack_device *idev; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (!dev) 908c2ecf20Sopenharmony_ci return -ENODEV; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci idev = to_ipack_dev(dev); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (add_uevent_var(env, 958c2ecf20Sopenharmony_ci "MODALIAS=ipack:f%02Xv%08Xd%08X", idev->id_format, 968c2ecf20Sopenharmony_ci idev->id_vendor, idev->id_device)) 978c2ecf20Sopenharmony_ci return -ENOMEM; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci#define ipack_device_attr(field, format_string) \ 1038c2ecf20Sopenharmony_cistatic ssize_t \ 1048c2ecf20Sopenharmony_cifield##_show(struct device *dev, struct device_attribute *attr, \ 1058c2ecf20Sopenharmony_ci char *buf) \ 1068c2ecf20Sopenharmony_ci{ \ 1078c2ecf20Sopenharmony_ci struct ipack_device *idev = to_ipack_dev(dev); \ 1088c2ecf20Sopenharmony_ci return sprintf(buf, format_string, idev->field); \ 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic ssize_t id_show(struct device *dev, 1128c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci unsigned int i, c, l, s; 1158c2ecf20Sopenharmony_ci struct ipack_device *idev = to_ipack_dev(dev); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci switch (idev->id_format) { 1198c2ecf20Sopenharmony_ci case IPACK_ID_VERSION_1: 1208c2ecf20Sopenharmony_ci l = 0x7; s = 1; break; 1218c2ecf20Sopenharmony_ci case IPACK_ID_VERSION_2: 1228c2ecf20Sopenharmony_ci l = 0xf; s = 2; break; 1238c2ecf20Sopenharmony_ci default: 1248c2ecf20Sopenharmony_ci return -EIO; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci c = 0; 1278c2ecf20Sopenharmony_ci for (i = 0; i < idev->id_avail; i++) { 1288c2ecf20Sopenharmony_ci if (i > 0) { 1298c2ecf20Sopenharmony_ci if ((i & l) == 0) 1308c2ecf20Sopenharmony_ci buf[c++] = '\n'; 1318c2ecf20Sopenharmony_ci else if ((i & s) == 0) 1328c2ecf20Sopenharmony_ci buf[c++] = ' '; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci sprintf(&buf[c], "%02x", idev->id[i]); 1358c2ecf20Sopenharmony_ci c += 2; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci buf[c++] = '\n'; 1388c2ecf20Sopenharmony_ci return c; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic ssize_t 1428c2ecf20Sopenharmony_ciid_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct ipack_device *idev = to_ipack_dev(dev); 1458c2ecf20Sopenharmony_ci switch (idev->id_format) { 1468c2ecf20Sopenharmony_ci case IPACK_ID_VERSION_1: 1478c2ecf20Sopenharmony_ci return sprintf(buf, "0x%02x\n", idev->id_vendor); 1488c2ecf20Sopenharmony_ci case IPACK_ID_VERSION_2: 1498c2ecf20Sopenharmony_ci return sprintf(buf, "0x%06x\n", idev->id_vendor); 1508c2ecf20Sopenharmony_ci default: 1518c2ecf20Sopenharmony_ci return -EIO; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic ssize_t 1568c2ecf20Sopenharmony_ciid_device_show(struct device *dev, struct device_attribute *attr, char *buf) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct ipack_device *idev = to_ipack_dev(dev); 1598c2ecf20Sopenharmony_ci switch (idev->id_format) { 1608c2ecf20Sopenharmony_ci case IPACK_ID_VERSION_1: 1618c2ecf20Sopenharmony_ci return sprintf(buf, "0x%02x\n", idev->id_device); 1628c2ecf20Sopenharmony_ci case IPACK_ID_VERSION_2: 1638c2ecf20Sopenharmony_ci return sprintf(buf, "0x%04x\n", idev->id_device); 1648c2ecf20Sopenharmony_ci default: 1658c2ecf20Sopenharmony_ci return -EIO; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic ssize_t modalias_show(struct device *dev, struct device_attribute *attr, 1708c2ecf20Sopenharmony_ci char *buf) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct ipack_device *idev = to_ipack_dev(dev); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return sprintf(buf, "ipac:f%02Xv%08Xd%08X", idev->id_format, 1758c2ecf20Sopenharmony_ci idev->id_vendor, idev->id_device); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ciipack_device_attr(id_format, "0x%hhx\n"); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(id); 1818c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(id_device); 1828c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(id_format); 1838c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(id_vendor); 1848c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(modalias); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic struct attribute *ipack_attrs[] = { 1878c2ecf20Sopenharmony_ci &dev_attr_id.attr, 1888c2ecf20Sopenharmony_ci &dev_attr_id_device.attr, 1898c2ecf20Sopenharmony_ci &dev_attr_id_format.attr, 1908c2ecf20Sopenharmony_ci &dev_attr_id_vendor.attr, 1918c2ecf20Sopenharmony_ci &dev_attr_modalias.attr, 1928c2ecf20Sopenharmony_ci NULL, 1938c2ecf20Sopenharmony_ci}; 1948c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(ipack); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic struct bus_type ipack_bus_type = { 1978c2ecf20Sopenharmony_ci .name = "ipack", 1988c2ecf20Sopenharmony_ci .probe = ipack_bus_probe, 1998c2ecf20Sopenharmony_ci .match = ipack_bus_match, 2008c2ecf20Sopenharmony_ci .remove = ipack_bus_remove, 2018c2ecf20Sopenharmony_ci .dev_groups = ipack_groups, 2028c2ecf20Sopenharmony_ci .uevent = ipack_uevent, 2038c2ecf20Sopenharmony_ci}; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistruct ipack_bus_device *ipack_bus_register(struct device *parent, int slots, 2068c2ecf20Sopenharmony_ci const struct ipack_bus_ops *ops, 2078c2ecf20Sopenharmony_ci struct module *owner) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci int bus_nr; 2108c2ecf20Sopenharmony_ci struct ipack_bus_device *bus; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci bus = kzalloc(sizeof(*bus), GFP_KERNEL); 2138c2ecf20Sopenharmony_ci if (!bus) 2148c2ecf20Sopenharmony_ci return NULL; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci bus_nr = ida_simple_get(&ipack_ida, 0, 0, GFP_KERNEL); 2178c2ecf20Sopenharmony_ci if (bus_nr < 0) { 2188c2ecf20Sopenharmony_ci kfree(bus); 2198c2ecf20Sopenharmony_ci return NULL; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci bus->bus_nr = bus_nr; 2238c2ecf20Sopenharmony_ci bus->parent = parent; 2248c2ecf20Sopenharmony_ci bus->slots = slots; 2258c2ecf20Sopenharmony_ci bus->ops = ops; 2268c2ecf20Sopenharmony_ci bus->owner = owner; 2278c2ecf20Sopenharmony_ci return bus; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipack_bus_register); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic int ipack_unregister_bus_member(struct device *dev, void *data) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct ipack_device *idev = to_ipack_dev(dev); 2348c2ecf20Sopenharmony_ci struct ipack_bus_device *bus = data; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (idev->bus == bus) 2378c2ecf20Sopenharmony_ci ipack_device_del(idev); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return 1; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ciint ipack_bus_unregister(struct ipack_bus_device *bus) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci bus_for_each_dev(&ipack_bus_type, NULL, bus, 2458c2ecf20Sopenharmony_ci ipack_unregister_bus_member); 2468c2ecf20Sopenharmony_ci ida_simple_remove(&ipack_ida, bus->bus_nr); 2478c2ecf20Sopenharmony_ci kfree(bus); 2488c2ecf20Sopenharmony_ci return 0; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipack_bus_unregister); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ciint ipack_driver_register(struct ipack_driver *edrv, struct module *owner, 2538c2ecf20Sopenharmony_ci const char *name) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci edrv->driver.owner = owner; 2568c2ecf20Sopenharmony_ci edrv->driver.name = name; 2578c2ecf20Sopenharmony_ci edrv->driver.bus = &ipack_bus_type; 2588c2ecf20Sopenharmony_ci return driver_register(&edrv->driver); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipack_driver_register); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_civoid ipack_driver_unregister(struct ipack_driver *edrv) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci driver_unregister(&edrv->driver); 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipack_driver_unregister); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic u16 ipack_crc_byte(u16 crc, u8 c) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci int i; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci crc ^= c << 8; 2738c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) 2748c2ecf20Sopenharmony_ci crc = (crc << 1) ^ ((crc & 0x8000) ? 0x1021 : 0); 2758c2ecf20Sopenharmony_ci return crc; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci/* 2798c2ecf20Sopenharmony_ci * The algorithm in lib/crc-ccitt.c does not seem to apply since it uses the 2808c2ecf20Sopenharmony_ci * opposite bit ordering. 2818c2ecf20Sopenharmony_ci */ 2828c2ecf20Sopenharmony_cistatic u8 ipack_calc_crc1(struct ipack_device *dev) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci u8 c; 2858c2ecf20Sopenharmony_ci u16 crc; 2868c2ecf20Sopenharmony_ci unsigned int i; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci crc = 0xffff; 2898c2ecf20Sopenharmony_ci for (i = 0; i < dev->id_avail; i++) { 2908c2ecf20Sopenharmony_ci c = (i != 11) ? dev->id[i] : 0; 2918c2ecf20Sopenharmony_ci crc = ipack_crc_byte(crc, c); 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci crc = ~crc; 2948c2ecf20Sopenharmony_ci return crc & 0xff; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic u16 ipack_calc_crc2(struct ipack_device *dev) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci u8 c; 3008c2ecf20Sopenharmony_ci u16 crc; 3018c2ecf20Sopenharmony_ci unsigned int i; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci crc = 0xffff; 3048c2ecf20Sopenharmony_ci for (i = 0; i < dev->id_avail; i++) { 3058c2ecf20Sopenharmony_ci c = ((i != 0x18) && (i != 0x19)) ? dev->id[i] : 0; 3068c2ecf20Sopenharmony_ci crc = ipack_crc_byte(crc, c); 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci crc = ~crc; 3098c2ecf20Sopenharmony_ci return crc; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic void ipack_parse_id1(struct ipack_device *dev) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci u8 *id = dev->id; 3158c2ecf20Sopenharmony_ci u8 crc; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci dev->id_vendor = id[4]; 3188c2ecf20Sopenharmony_ci dev->id_device = id[5]; 3198c2ecf20Sopenharmony_ci dev->speed_8mhz = 1; 3208c2ecf20Sopenharmony_ci dev->speed_32mhz = (id[7] == 'H'); 3218c2ecf20Sopenharmony_ci crc = ipack_calc_crc1(dev); 3228c2ecf20Sopenharmony_ci dev->id_crc_correct = (crc == id[11]); 3238c2ecf20Sopenharmony_ci if (!dev->id_crc_correct) { 3248c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "ID CRC invalid found 0x%x, expected 0x%x.\n", 3258c2ecf20Sopenharmony_ci id[11], crc); 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic void ipack_parse_id2(struct ipack_device *dev) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci __be16 *id = (__be16 *) dev->id; 3328c2ecf20Sopenharmony_ci u16 flags, crc; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci dev->id_vendor = ((be16_to_cpu(id[3]) & 0xff) << 16) 3358c2ecf20Sopenharmony_ci + be16_to_cpu(id[4]); 3368c2ecf20Sopenharmony_ci dev->id_device = be16_to_cpu(id[5]); 3378c2ecf20Sopenharmony_ci flags = be16_to_cpu(id[10]); 3388c2ecf20Sopenharmony_ci dev->speed_8mhz = !!(flags & 2); 3398c2ecf20Sopenharmony_ci dev->speed_32mhz = !!(flags & 4); 3408c2ecf20Sopenharmony_ci crc = ipack_calc_crc2(dev); 3418c2ecf20Sopenharmony_ci dev->id_crc_correct = (crc == be16_to_cpu(id[12])); 3428c2ecf20Sopenharmony_ci if (!dev->id_crc_correct) { 3438c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "ID CRC invalid found 0x%x, expected 0x%x.\n", 3448c2ecf20Sopenharmony_ci id[11], crc); 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic int ipack_device_read_id(struct ipack_device *dev) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci u8 __iomem *idmem; 3518c2ecf20Sopenharmony_ci int i; 3528c2ecf20Sopenharmony_ci int ret = 0; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci idmem = ioremap(dev->region[IPACK_ID_SPACE].start, 3558c2ecf20Sopenharmony_ci dev->region[IPACK_ID_SPACE].size); 3568c2ecf20Sopenharmony_ci if (!idmem) { 3578c2ecf20Sopenharmony_ci dev_err(&dev->dev, "error mapping memory\n"); 3588c2ecf20Sopenharmony_ci return -ENOMEM; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* Determine ID PROM Data Format. If we find the ids "IPAC" or "IPAH" 3628c2ecf20Sopenharmony_ci * we are dealing with a IndustryPack format 1 device. If we detect 3638c2ecf20Sopenharmony_ci * "VITA4 " (16 bit big endian formatted) we are dealing with a 3648c2ecf20Sopenharmony_ci * IndustryPack format 2 device */ 3658c2ecf20Sopenharmony_ci if ((ioread8(idmem + 1) == 'I') && 3668c2ecf20Sopenharmony_ci (ioread8(idmem + 3) == 'P') && 3678c2ecf20Sopenharmony_ci (ioread8(idmem + 5) == 'A') && 3688c2ecf20Sopenharmony_ci ((ioread8(idmem + 7) == 'C') || 3698c2ecf20Sopenharmony_ci (ioread8(idmem + 7) == 'H'))) { 3708c2ecf20Sopenharmony_ci dev->id_format = IPACK_ID_VERSION_1; 3718c2ecf20Sopenharmony_ci dev->id_avail = ioread8(idmem + 0x15); 3728c2ecf20Sopenharmony_ci if ((dev->id_avail < 0x0c) || (dev->id_avail > 0x40)) { 3738c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "invalid id size"); 3748c2ecf20Sopenharmony_ci dev->id_avail = 0x0c; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci } else if ((ioread8(idmem + 0) == 'I') && 3778c2ecf20Sopenharmony_ci (ioread8(idmem + 1) == 'V') && 3788c2ecf20Sopenharmony_ci (ioread8(idmem + 2) == 'A') && 3798c2ecf20Sopenharmony_ci (ioread8(idmem + 3) == 'T') && 3808c2ecf20Sopenharmony_ci (ioread8(idmem + 4) == ' ') && 3818c2ecf20Sopenharmony_ci (ioread8(idmem + 5) == '4')) { 3828c2ecf20Sopenharmony_ci dev->id_format = IPACK_ID_VERSION_2; 3838c2ecf20Sopenharmony_ci dev->id_avail = ioread16be(idmem + 0x16); 3848c2ecf20Sopenharmony_ci if ((dev->id_avail < 0x1a) || (dev->id_avail > 0x40)) { 3858c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "invalid id size"); 3868c2ecf20Sopenharmony_ci dev->id_avail = 0x1a; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci } else { 3898c2ecf20Sopenharmony_ci dev->id_format = IPACK_ID_VERSION_INVALID; 3908c2ecf20Sopenharmony_ci dev->id_avail = 0; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (!dev->id_avail) { 3948c2ecf20Sopenharmony_ci ret = -ENODEV; 3958c2ecf20Sopenharmony_ci goto out; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* Obtain the amount of memory required to store a copy of the complete 3998c2ecf20Sopenharmony_ci * ID ROM contents */ 4008c2ecf20Sopenharmony_ci dev->id = kmalloc(dev->id_avail, GFP_KERNEL); 4018c2ecf20Sopenharmony_ci if (!dev->id) { 4028c2ecf20Sopenharmony_ci ret = -ENOMEM; 4038c2ecf20Sopenharmony_ci goto out; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci for (i = 0; i < dev->id_avail; i++) { 4068c2ecf20Sopenharmony_ci if (dev->id_format == IPACK_ID_VERSION_1) 4078c2ecf20Sopenharmony_ci dev->id[i] = ioread8(idmem + (i << 1) + 1); 4088c2ecf20Sopenharmony_ci else 4098c2ecf20Sopenharmony_ci dev->id[i] = ioread8(idmem + i); 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci /* now we can finally work with the copy */ 4138c2ecf20Sopenharmony_ci switch (dev->id_format) { 4148c2ecf20Sopenharmony_ci case IPACK_ID_VERSION_1: 4158c2ecf20Sopenharmony_ci ipack_parse_id1(dev); 4168c2ecf20Sopenharmony_ci break; 4178c2ecf20Sopenharmony_ci case IPACK_ID_VERSION_2: 4188c2ecf20Sopenharmony_ci ipack_parse_id2(dev); 4198c2ecf20Sopenharmony_ci break; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ciout: 4238c2ecf20Sopenharmony_ci iounmap(idmem); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci return ret; 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ciint ipack_device_init(struct ipack_device *dev) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci int ret; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci dev->dev.bus = &ipack_bus_type; 4338c2ecf20Sopenharmony_ci dev->dev.release = ipack_device_release; 4348c2ecf20Sopenharmony_ci dev->dev.parent = dev->bus->parent; 4358c2ecf20Sopenharmony_ci dev_set_name(&dev->dev, 4368c2ecf20Sopenharmony_ci "ipack-dev.%u.%u", dev->bus->bus_nr, dev->slot); 4378c2ecf20Sopenharmony_ci device_initialize(&dev->dev); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci if (dev->bus->ops->set_clockrate(dev, 8)) 4408c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "failed to switch to 8 MHz operation for reading of device ID.\n"); 4418c2ecf20Sopenharmony_ci if (dev->bus->ops->reset_timeout(dev)) 4428c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "failed to reset potential timeout."); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci ret = ipack_device_read_id(dev); 4458c2ecf20Sopenharmony_ci if (ret < 0) { 4468c2ecf20Sopenharmony_ci dev_err(&dev->dev, "error reading device id section.\n"); 4478c2ecf20Sopenharmony_ci return ret; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* if the device supports 32 MHz operation, use it. */ 4518c2ecf20Sopenharmony_ci if (dev->speed_32mhz) { 4528c2ecf20Sopenharmony_ci ret = dev->bus->ops->set_clockrate(dev, 32); 4538c2ecf20Sopenharmony_ci if (ret < 0) 4548c2ecf20Sopenharmony_ci dev_err(&dev->dev, "failed to switch to 32 MHz operation.\n"); 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci return 0; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipack_device_init); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ciint ipack_device_add(struct ipack_device *dev) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci return device_add(&dev->dev); 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipack_device_add); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_civoid ipack_device_del(struct ipack_device *dev) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci device_del(&dev->dev); 4708c2ecf20Sopenharmony_ci ipack_put_device(dev); 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipack_device_del); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_civoid ipack_get_device(struct ipack_device *dev) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci get_device(&dev->dev); 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipack_get_device); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_civoid ipack_put_device(struct ipack_device *dev) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci put_device(&dev->dev); 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipack_put_device); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic int __init ipack_init(void) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci ida_init(&ipack_ida); 4898c2ecf20Sopenharmony_ci return bus_register(&ipack_bus_type); 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic void __exit ipack_exit(void) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci bus_unregister(&ipack_bus_type); 4958c2ecf20Sopenharmony_ci ida_destroy(&ipack_ida); 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cimodule_init(ipack_init); 4998c2ecf20Sopenharmony_cimodule_exit(ipack_exit); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ciMODULE_AUTHOR("Samuel Iglesias Gonsalvez <siglesias@igalia.com>"); 5028c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5038c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Industry-pack bus core"); 504