367 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * tifm_core.c - TI FlashMedia driver4 *5 * Copyright (C) 2006 Alex Dubov <oakad@yahoo.com>6 */7 8#include <linux/tifm.h>9#include <linux/slab.h>10#include <linux/init.h>11#include <linux/idr.h>12#include <linux/module.h>13 14#define DRIVER_NAME "tifm_core"15#define DRIVER_VERSION "0.8"16 17static struct workqueue_struct *workqueue;18static DEFINE_IDR(tifm_adapter_idr);19static DEFINE_SPINLOCK(tifm_adapter_lock);20 21static const char *tifm_media_type_name(unsigned char type, unsigned char nt)22{23 const char *card_type_name[3][3] = {24 { "SmartMedia/xD", "MemoryStick", "MMC/SD" },25 { "XD", "MS", "SD"},26 { "xd", "ms", "sd"}27 };28 29 if (nt > 2 || type < 1 || type > 3)30 return NULL;31 return card_type_name[nt][type - 1];32}33 34static int tifm_dev_match(struct tifm_dev *sock, struct tifm_device_id *id)35{36 if (sock->type == id->type)37 return 1;38 return 0;39}40 41static int tifm_bus_match(struct device *dev, const struct device_driver *drv)42{43 struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);44 const struct tifm_driver *fm_drv = container_of_const(drv, struct tifm_driver,45 driver);46 struct tifm_device_id *ids = fm_drv->id_table;47 48 if (ids) {49 while (ids->type) {50 if (tifm_dev_match(sock, ids))51 return 1;52 ++ids;53 }54 }55 return 0;56}57 58static int tifm_uevent(const struct device *dev, struct kobj_uevent_env *env)59{60 const struct tifm_dev *sock = container_of_const(dev, struct tifm_dev, dev);61 62 if (add_uevent_var(env, "TIFM_CARD_TYPE=%s", tifm_media_type_name(sock->type, 1)))63 return -ENOMEM;64 65 return 0;66}67 68static int tifm_device_probe(struct device *dev)69{70 struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);71 struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver,72 driver);73 int rc = -ENODEV;74 75 get_device(dev);76 if (dev->driver && drv->probe) {77 rc = drv->probe(sock);78 if (!rc)79 return 0;80 }81 put_device(dev);82 return rc;83}84 85static void tifm_dummy_event(struct tifm_dev *sock)86{87 return;88}89 90static void tifm_device_remove(struct device *dev)91{92 struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);93 struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver,94 driver);95 96 if (dev->driver && drv->remove) {97 sock->card_event = tifm_dummy_event;98 sock->data_event = tifm_dummy_event;99 drv->remove(sock);100 sock->dev.driver = NULL;101 }102 103 put_device(dev);104}105 106#ifdef CONFIG_PM107 108static int tifm_device_suspend(struct device *dev, pm_message_t state)109{110 struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);111 struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver,112 driver);113 114 if (dev->driver && drv->suspend)115 return drv->suspend(sock, state);116 return 0;117}118 119static int tifm_device_resume(struct device *dev)120{121 struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);122 struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver,123 driver);124 125 if (dev->driver && drv->resume)126 return drv->resume(sock);127 return 0;128}129 130#else131 132#define tifm_device_suspend NULL133#define tifm_device_resume NULL134 135#endif /* CONFIG_PM */136 137static ssize_t type_show(struct device *dev, struct device_attribute *attr,138 char *buf)139{140 struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);141 return sprintf(buf, "%x", sock->type);142}143static DEVICE_ATTR_RO(type);144 145static struct attribute *tifm_dev_attrs[] = {146 &dev_attr_type.attr,147 NULL,148};149ATTRIBUTE_GROUPS(tifm_dev);150 151static const struct bus_type tifm_bus_type = {152 .name = "tifm",153 .dev_groups = tifm_dev_groups,154 .match = tifm_bus_match,155 .uevent = tifm_uevent,156 .probe = tifm_device_probe,157 .remove = tifm_device_remove,158 .suspend = tifm_device_suspend,159 .resume = tifm_device_resume160};161 162static void tifm_free(struct device *dev)163{164 struct tifm_adapter *fm = container_of(dev, struct tifm_adapter, dev);165 166 kfree(fm);167}168 169static const struct class tifm_adapter_class = {170 .name = "tifm_adapter",171 .dev_release = tifm_free172};173 174struct tifm_adapter *tifm_alloc_adapter(unsigned int num_sockets,175 struct device *dev)176{177 struct tifm_adapter *fm;178 179 fm = kzalloc(struct_size(fm, sockets, num_sockets), GFP_KERNEL);180 if (fm) {181 fm->dev.class = &tifm_adapter_class;182 fm->dev.parent = dev;183 device_initialize(&fm->dev);184 spin_lock_init(&fm->lock);185 fm->num_sockets = num_sockets;186 }187 return fm;188}189EXPORT_SYMBOL(tifm_alloc_adapter);190 191int tifm_add_adapter(struct tifm_adapter *fm)192{193 int rc;194 195 idr_preload(GFP_KERNEL);196 spin_lock(&tifm_adapter_lock);197 rc = idr_alloc(&tifm_adapter_idr, fm, 0, 0, GFP_NOWAIT);198 if (rc >= 0)199 fm->id = rc;200 spin_unlock(&tifm_adapter_lock);201 idr_preload_end();202 if (rc < 0)203 return rc;204 205 dev_set_name(&fm->dev, "tifm%u", fm->id);206 rc = device_add(&fm->dev);207 if (rc) {208 spin_lock(&tifm_adapter_lock);209 idr_remove(&tifm_adapter_idr, fm->id);210 spin_unlock(&tifm_adapter_lock);211 }212 213 return rc;214}215EXPORT_SYMBOL(tifm_add_adapter);216 217void tifm_remove_adapter(struct tifm_adapter *fm)218{219 unsigned int cnt;220 221 flush_workqueue(workqueue);222 for (cnt = 0; cnt < fm->num_sockets; ++cnt) {223 if (fm->sockets[cnt])224 device_unregister(&fm->sockets[cnt]->dev);225 }226 227 spin_lock(&tifm_adapter_lock);228 idr_remove(&tifm_adapter_idr, fm->id);229 spin_unlock(&tifm_adapter_lock);230 device_del(&fm->dev);231}232EXPORT_SYMBOL(tifm_remove_adapter);233 234void tifm_free_adapter(struct tifm_adapter *fm)235{236 put_device(&fm->dev);237}238EXPORT_SYMBOL(tifm_free_adapter);239 240void tifm_free_device(struct device *dev)241{242 struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);243 kfree(sock);244}245EXPORT_SYMBOL(tifm_free_device);246 247struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm, unsigned int id,248 unsigned char type)249{250 struct tifm_dev *sock = NULL;251 252 if (!tifm_media_type_name(type, 0))253 return sock;254 255 sock = kzalloc(sizeof(struct tifm_dev), GFP_KERNEL);256 if (sock) {257 spin_lock_init(&sock->lock);258 sock->type = type;259 sock->socket_id = id;260 sock->card_event = tifm_dummy_event;261 sock->data_event = tifm_dummy_event;262 263 sock->dev.parent = fm->dev.parent;264 sock->dev.bus = &tifm_bus_type;265 sock->dev.dma_mask = fm->dev.parent->dma_mask;266 sock->dev.release = tifm_free_device;267 268 dev_set_name(&sock->dev, "tifm_%s%u:%u",269 tifm_media_type_name(type, 2), fm->id, id);270 printk(KERN_INFO DRIVER_NAME271 ": %s card detected in socket %u:%u\n",272 tifm_media_type_name(type, 0), fm->id, id);273 }274 return sock;275}276EXPORT_SYMBOL(tifm_alloc_device);277 278void tifm_eject(struct tifm_dev *sock)279{280 struct tifm_adapter *fm = dev_get_drvdata(sock->dev.parent);281 fm->eject(fm, sock);282}283EXPORT_SYMBOL(tifm_eject);284 285int tifm_has_ms_pif(struct tifm_dev *sock)286{287 struct tifm_adapter *fm = dev_get_drvdata(sock->dev.parent);288 return fm->has_ms_pif(fm, sock);289}290EXPORT_SYMBOL(tifm_has_ms_pif);291 292int tifm_map_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,293 int direction)294{295 return dma_map_sg(&to_pci_dev(sock->dev.parent)->dev, sg, nents,296 direction);297}298EXPORT_SYMBOL(tifm_map_sg);299 300void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,301 int direction)302{303 dma_unmap_sg(&to_pci_dev(sock->dev.parent)->dev, sg, nents, direction);304}305EXPORT_SYMBOL(tifm_unmap_sg);306 307void tifm_queue_work(struct work_struct *work)308{309 queue_work(workqueue, work);310}311EXPORT_SYMBOL(tifm_queue_work);312 313int tifm_register_driver(struct tifm_driver *drv)314{315 drv->driver.bus = &tifm_bus_type;316 317 return driver_register(&drv->driver);318}319EXPORT_SYMBOL(tifm_register_driver);320 321void tifm_unregister_driver(struct tifm_driver *drv)322{323 driver_unregister(&drv->driver);324}325EXPORT_SYMBOL(tifm_unregister_driver);326 327static int __init tifm_init(void)328{329 int rc;330 331 workqueue = create_freezable_workqueue("tifm");332 if (!workqueue)333 return -ENOMEM;334 335 rc = bus_register(&tifm_bus_type);336 337 if (rc)338 goto err_out_wq;339 340 rc = class_register(&tifm_adapter_class);341 if (!rc)342 return 0;343 344 bus_unregister(&tifm_bus_type);345 346err_out_wq:347 destroy_workqueue(workqueue);348 349 return rc;350}351 352static void __exit tifm_exit(void)353{354 class_unregister(&tifm_adapter_class);355 bus_unregister(&tifm_bus_type);356 destroy_workqueue(workqueue);357}358 359subsys_initcall(tifm_init);360module_exit(tifm_exit);361 362MODULE_LICENSE("GPL");363MODULE_AUTHOR("Alex Dubov");364MODULE_DESCRIPTION("TI FlashMedia core driver");365MODULE_LICENSE("GPL");366MODULE_VERSION(DRIVER_VERSION);367