📄 mss_core.c
字号:
/* Core MMC driver functions * * Copyright (c) 2002 Hewlett-Packard Company * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * Many thanks to Alessandro Rubini and Jonathan Corbet! * * Author: Andrew Christian * 6 May 2002 *(C) Copyright 2006 Marvell International Ltd. * All Rights Reserved *//* * mss_core.c - MMC/SD/SDIO Core driver * * Copyright (C) 2006 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/version.h>#include <linux/proc_fs.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/list.h>#include <linux/sysctl.h>#include <linux/suspend.h>#include <asm/uaccess.h>#include <asm/hardware.h>#include <asm/dma.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/sizes.h>#include <asm/types.h>#include <linux/mmc/mss_core.h>static LIST_HEAD(mss_protocol_list);static LIST_HEAD(mss_host_list);static inline void mss_delay(unsigned int ms){ if (ms < HZ / 1000) { yield(); mdelay(ms); } else { msleep_interruptible(ms); }}static void mss_power_up(struct mss_host *host){ struct mss_ios ios; memcpy(&ios, &host->ios, sizeof(ios)); ios.vdd = host->vdd; ios.chip_select = MSS_CS_NO_CARE; ios.power_mode = MSS_POWER_UP; host->ops->set_ios(host, &ios); mss_delay(1); ios.clock = host->f_min; ios.power_mode = MSS_POWER_ON; host->ops->set_ios(host, &ios); mss_delay(2);}static void mss_power_off(struct mss_host *host){ struct mss_ios ios; memcpy(&ios, &host->ios, sizeof(ios)); ios.clock = 0; ios.chip_select = MSS_CS_NO_CARE; ios.power_mode = MSS_POWER_OFF; host->ops->set_ios(host, &ios);}static void mss_idle_cards(struct mss_host *host){ struct mss_ios ios; memcpy(&ios, &host->ios, sizeof(ios)); ios.chip_select = MSS_CS_HIGH; host->ops->set_ios(host, &ios); mss_delay(1); ios.chip_select = MSS_CS_NO_CARE; host->ops->set_ios(host, &ios); mss_delay(1);}/* * Only after card is initialized by protocol and be registed to mmc_bus, the * state is changed to MSS_CARD_REGISTERED. */static int mmc_bus_match(struct device *dev, struct device_driver *drv){ struct mss_card *card; card = container_of(dev, struct mss_card, dev); /* when card->state is MSS_CARD_REGISTERED,it is accepted by protocol */ if (card->prot_driver && (card->state & MSS_CARD_REGISTERED)) return 1; return 0; }static int mmc_bus_hotplug(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size){ printk(KERN_INFO "*** HOT PLUG ***\n"); return 0;}static int mmc_bus_suspend(struct device * dev, pm_message_t state){ int ret = 0; struct mss_card *card; card = container_of(dev, struct mss_card, dev); dbg("card name :%s, state:%d\n", card->dev.bus_id, card->state); /*if (card->state & MSS_CARD_HANDLEIO) return -EAGAIN;*/ if (card->state & MSS_CARD_SUSPENDED) return 0; dbg("dev driver:%p, suspend%p\n", dev->driver, (dev->driver)? dev->driver->suspend:NULL); if (dev->driver && dev->driver->suspend) { ret = dev->driver->suspend(dev, state, SUSPEND_DISABLE); if (ret == 0) ret = dev->driver->suspend(dev, state, SUSPEND_SAVE_STATE); if (ret == 0) ret = dev->driver->suspend(dev, state, SUSPEND_POWER_DOWN); } if (ret == 0) { /* mark MSS_CARD_SUSPEND here */ card->state |= MSS_CARD_SUSPENDED; } return ret;}/* * card may be removed or replaced by another card from the mmc_bus when it is * sleeping, and the slot may be inserted a card when it is sleeping. * The controller resume function need to take care about it. */static int mmc_bus_resume(struct device * dev){ int ret = 0; struct mss_card *card; card = container_of(dev, struct mss_card, dev); dbg("card name :%s, state:%d\n", card->dev.bus_id, card->state); /* it is new instered card or replaced card */ if (!(card->state & MSS_CARD_SUSPENDED)) return 0; card->state &= ~MSS_CARD_SUSPENDED; dbg("dev driver:%p, resume;%p\n", dev->driver, (dev->driver) ? dev->driver->resume:NULL); if (dev->driver && dev->driver->resume) { ret = dev->driver->resume(dev, RESUME_POWER_ON); if (ret == 0) ret = dev->driver->resume(dev, RESUME_RESTORE_STATE); if (ret == 0) ret = dev->driver->resume(dev, RESUME_ENABLE); } return ret;}static struct bus_type mmc_bus_type = { .name = "mmc_bus", .match = mmc_bus_match, .hotplug = mmc_bus_hotplug, .suspend = mmc_bus_suspend, .resume = mmc_bus_resume,};static void mss_card_device_release(struct device *dev){ struct mss_card *card = container_of(dev, struct mss_card, dev); kfree(card);}static void mss_claim_host(struct mss_host *host, struct mss_card *card){ DECLARE_WAITQUEUE(wait, current); unsigned long flags; spin_lock_irqsave(&host->lock, flags); while (host->active_card != NULL) { spin_unlock_irqrestore(&host->lock, flags); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&host->wq, &wait); schedule(); set_current_state(TASK_RUNNING); remove_wait_queue(&host->wq, &wait); spin_lock_irqsave(&host->lock, flags); } host->active_card = card; spin_unlock_irqrestore(&host->lock, flags);}static void mss_release_host(struct mss_host* host){ unsigned long flags; BUG_ON(host->active_card == NULL); spin_lock_irqsave(&host->lock, flags); host->active_card = NULL; spin_unlock_irqrestore(&host->lock, flags); wake_up(&host->wq);}int mss_card_get(struct mss_card *card){ if ((card->state & MSS_CARD_REMOVING) || !(card->state & MSS_CARD_REGISTERED)) return -ENXIO; if (!get_device(&card->dev)) return -ENXIO; return 0;}void mss_card_put(struct mss_card *card){ put_device(&card->dev);}/* * finish handling a request. */static void mss_finish_request(struct mss_request *req){ struct mss_driver *drv; struct mss_card *card = req->card; drv = container_of(card->dev.driver, struct mss_driver, driver); if (drv && drv->request_done) drv->request_done(req);}/* * Loop all the protocol in the mss_protocol_list, and find one protocol that * can successful recognize and init the card. */static int mss_attach_protocol(struct mss_card *card){ struct list_head *item; struct mss_prot_driver *pdrv = NULL; struct mss_host *host = card->slot->host; int ret; /* loop all the protocol, and find one that match the card */ list_for_each(item, &mss_protocol_list) { pdrv = list_entry(item, struct mss_prot_driver, node); ret = pdrv->attach_card(card); if (ret) continue; mss_claim_host(host, card); ret = pdrv->prot_entry(card, MSS_RECOGNIZE_CARD, NULL, NULL); mss_release_host(host); if (ret) continue; dbg("mss_attach_protocol: card type: %d \n",card->card_type); switch (card->card_type) { case MSS_MMC_CARD: //case MSS_CE_ATA: case MSS_SD_CARD: case MSS_SDIO_CARD: case MSS_COMBO_CARD: goto identified; /* * The card can be recognized, but it deos not fit the * controller. */ case MSS_UNCOMPATIBLE_CARD: pdrv->detach_card(card); return MSS_ERROR_NO_PROTOCOL; /* The card can not be recognized by the protocl */ case MSS_UNKNOWN_CARD: pdrv->detach_card(card); break; default: pdrv->detach_card(card); printk(KERN_WARNING "protocol driver :%s " "return unknown value when" " recognize the card\n", pdrv->name); break; } } return MSS_ERROR_NO_PROTOCOL;identified: card->prot_driver = pdrv; return 0;}/* Initialize card by the protocol */int mss_init_card(struct mss_card *card){ int ret; struct mss_host *host = card->slot->host; if (!card || !card->prot_driver) return -EINVAL; mss_claim_host(host, card); ret = card->prot_driver->prot_entry(card, MSS_INIT_CARD, NULL, NULL); mss_release_host(host); return ret;}int mss_query_card(struct mss_card *card){ int ret; struct mss_host *host = card->slot->host; if (!card || !card->prot_driver) return -EINVAL; mss_claim_host(host, card); ret = card->prot_driver->prot_entry(card, MSS_QUERY_CARD, NULL, NULL); mss_release_host(host); return ret;}static int __mss_insert_card(struct mss_card *card) { int ret; /* Step 1: Recognize the card */ ret = mss_attach_protocol(card); if (ret) return ret; /* Step 2, initialize the card */ ret = mss_init_card(card); if (ret) { goto detach_prot; } card->state |= MSS_CARD_INITED; /* Step 3, register the card to mmc bus */ card->dev.release = mss_card_device_release; card->dev.parent = card->slot->host->dev; /* set bus_id and name */ snprintf(&card->dev.bus_id[0], sizeof(card->dev.bus_id), "mmc%d%d", card->slot->host->id, card->slot->id); card->dev.bus = &mmc_bus_type; card->state |= MSS_CARD_REGISTERED; ret = device_register(&card->dev); /* will call mss_card_probe */ if (ret) { ret = MSS_ERROR_REGISTER_CARD; card->state &= ~MSS_CARD_REGISTERED; goto detach_prot; } return MSS_ERROR_NONE; detach_prot: card->prot_driver->detach_card(card); card->prot_driver = NULL; return ret;}/* * After knowing a card has been inserted into the slot, this function should * be invoked. At last, load card driver in card (done by card_driver->probe). */static int mss_insert_card(struct mss_slot *slot){ struct mss_card * card; int ret; BUG_ON(slot->card); card = kzalloc(sizeof(struct mss_card), GFP_KERNEL); if (!card) return -ENOMEM; card->slot = slot; slot->card = card; ret = __mss_insert_card(card); if (ret) { slot->card = NULL; kfree(card); } return ret;}static int __mss_eject_card(struct mss_card *card){ card->state |= MSS_CARD_REMOVING; if (card->state & MSS_CARD_REGISTERED) { device_unregister(&(card->dev)); card->state &= ~MSS_CARD_REGISTERED; } if (card->prot_driver) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -