xref: /kernel/linux/linux-5.10/drivers/ipack/ipack.c (revision 8c2ecf20)
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