Path: blob/master/drivers/memstick/host/jmb38x_ms.c
15111 views
/*1* jmb38x_ms.c - JMicron jmb38x MemoryStick card reader2*3* Copyright (C) 2008 Alex Dubov <[email protected]>4*5* This program is free software; you can redistribute it and/or modify6* it under the terms of the GNU General Public License version 2 as7* published by the Free Software Foundation.8*9*/1011#include <linux/spinlock.h>12#include <linux/interrupt.h>13#include <linux/pci.h>14#include <linux/dma-mapping.h>15#include <linux/delay.h>16#include <linux/highmem.h>17#include <linux/memstick.h>18#include <linux/slab.h>1920#define DRIVER_NAME "jmb38x_ms"2122static int no_dma;23module_param(no_dma, bool, 0644);2425enum {26DMA_ADDRESS = 0x00,27BLOCK = 0x04,28DMA_CONTROL = 0x08,29TPC_P0 = 0x0c,30TPC_P1 = 0x10,31TPC = 0x14,32HOST_CONTROL = 0x18,33DATA = 0x1c,34STATUS = 0x20,35INT_STATUS = 0x24,36INT_STATUS_ENABLE = 0x28,37INT_SIGNAL_ENABLE = 0x2c,38TIMER = 0x30,39TIMER_CONTROL = 0x34,40PAD_OUTPUT_ENABLE = 0x38,41PAD_PU_PD = 0x3c,42CLOCK_DELAY = 0x40,43ADMA_ADDRESS = 0x44,44CLOCK_CONTROL = 0x48,45LED_CONTROL = 0x4c,46VERSION = 0x5047};4849struct jmb38x_ms_host {50struct jmb38x_ms *chip;51void __iomem *addr;52spinlock_t lock;53struct tasklet_struct notify;54int id;55char host_id[32];56int irq;57unsigned int block_pos;58unsigned long timeout_jiffies;59struct timer_list timer;60struct memstick_request *req;61unsigned char cmd_flags;62unsigned char io_pos;63unsigned char ifmode;64unsigned int io_word[2];65};6667struct jmb38x_ms {68struct pci_dev *pdev;69int host_cnt;70struct memstick_host *hosts[];71};7273#define BLOCK_COUNT_MASK 0xffff000074#define BLOCK_SIZE_MASK 0x00000fff7576#define DMA_CONTROL_ENABLE 0x000000017778#define TPC_DATA_SEL 0x0000800079#define TPC_DIR 0x0000400080#define TPC_WAIT_INT 0x0000200081#define TPC_GET_INT 0x0000080082#define TPC_CODE_SZ_MASK 0x0000070083#define TPC_DATA_SZ_MASK 0x000000078485#define HOST_CONTROL_TDELAY_EN 0x0004000086#define HOST_CONTROL_HW_OC_P 0x0001000087#define HOST_CONTROL_RESET_REQ 0x0000800088#define HOST_CONTROL_REI 0x0000400089#define HOST_CONTROL_LED 0x0000040090#define HOST_CONTROL_FAST_CLK 0x0000020091#define HOST_CONTROL_RESET 0x0000010092#define HOST_CONTROL_POWER_EN 0x0000008093#define HOST_CONTROL_CLOCK_EN 0x0000004094#define HOST_CONTROL_REO 0x0000000895#define HOST_CONTROL_IF_SHIFT 49697#define HOST_CONTROL_IF_SERIAL 0x098#define HOST_CONTROL_IF_PAR4 0x199#define HOST_CONTROL_IF_PAR8 0x3100101#define STATUS_BUSY 0x00080000102#define STATUS_MS_DAT7 0x00040000103#define STATUS_MS_DAT6 0x00020000104#define STATUS_MS_DAT5 0x00010000105#define STATUS_MS_DAT4 0x00008000106#define STATUS_MS_DAT3 0x00004000107#define STATUS_MS_DAT2 0x00002000108#define STATUS_MS_DAT1 0x00001000109#define STATUS_MS_DAT0 0x00000800110#define STATUS_HAS_MEDIA 0x00000400111#define STATUS_FIFO_EMPTY 0x00000200112#define STATUS_FIFO_FULL 0x00000100113#define STATUS_MS_CED 0x00000080114#define STATUS_MS_ERR 0x00000040115#define STATUS_MS_BRQ 0x00000020116#define STATUS_MS_CNK 0x00000001117118#define INT_STATUS_TPC_ERR 0x00080000119#define INT_STATUS_CRC_ERR 0x00040000120#define INT_STATUS_TIMER_TO 0x00020000121#define INT_STATUS_HSK_TO 0x00010000122#define INT_STATUS_ANY_ERR 0x00008000123#define INT_STATUS_FIFO_WRDY 0x00000080124#define INT_STATUS_FIFO_RRDY 0x00000040125#define INT_STATUS_MEDIA_OUT 0x00000010126#define INT_STATUS_MEDIA_IN 0x00000008127#define INT_STATUS_DMA_BOUNDARY 0x00000004128#define INT_STATUS_EOTRAN 0x00000002129#define INT_STATUS_EOTPC 0x00000001130131#define INT_STATUS_ALL 0x000f801f132133#define PAD_OUTPUT_ENABLE_MS 0x0F3F134135#define PAD_PU_PD_OFF 0x7FFF0000136#define PAD_PU_PD_ON_MS_SOCK0 0x5f8f0000137#define PAD_PU_PD_ON_MS_SOCK1 0x0f0f0000138139#define CLOCK_CONTROL_BY_MMIO 0x00000008140#define CLOCK_CONTROL_40MHZ 0x00000001141#define CLOCK_CONTROL_50MHZ 0x00000002142#define CLOCK_CONTROL_60MHZ 0x00000010143#define CLOCK_CONTROL_62_5MHZ 0x00000004144#define CLOCK_CONTROL_OFF 0x00000000145146#define PCI_CTL_CLOCK_DLY_ADDR 0x000000b0147148enum {149CMD_READY = 0x01,150FIFO_READY = 0x02,151REG_DATA = 0x04,152DMA_DATA = 0x08153};154155static unsigned int jmb38x_ms_read_data(struct jmb38x_ms_host *host,156unsigned char *buf, unsigned int length)157{158unsigned int off = 0;159160while (host->io_pos && length) {161buf[off++] = host->io_word[0] & 0xff;162host->io_word[0] >>= 8;163length--;164host->io_pos--;165}166167if (!length)168return off;169170while (!(STATUS_FIFO_EMPTY & readl(host->addr + STATUS))) {171if (length < 4)172break;173*(unsigned int *)(buf + off) = __raw_readl(host->addr + DATA);174length -= 4;175off += 4;176}177178if (length179&& !(STATUS_FIFO_EMPTY & readl(host->addr + STATUS))) {180host->io_word[0] = readl(host->addr + DATA);181for (host->io_pos = 4; host->io_pos; --host->io_pos) {182buf[off++] = host->io_word[0] & 0xff;183host->io_word[0] >>= 8;184length--;185if (!length)186break;187}188}189190return off;191}192193static unsigned int jmb38x_ms_read_reg_data(struct jmb38x_ms_host *host,194unsigned char *buf,195unsigned int length)196{197unsigned int off = 0;198199while (host->io_pos > 4 && length) {200buf[off++] = host->io_word[0] & 0xff;201host->io_word[0] >>= 8;202length--;203host->io_pos--;204}205206if (!length)207return off;208209while (host->io_pos && length) {210buf[off++] = host->io_word[1] & 0xff;211host->io_word[1] >>= 8;212length--;213host->io_pos--;214}215216return off;217}218219static unsigned int jmb38x_ms_write_data(struct jmb38x_ms_host *host,220unsigned char *buf,221unsigned int length)222{223unsigned int off = 0;224225if (host->io_pos) {226while (host->io_pos < 4 && length) {227host->io_word[0] |= buf[off++] << (host->io_pos * 8);228host->io_pos++;229length--;230}231}232233if (host->io_pos == 4234&& !(STATUS_FIFO_FULL & readl(host->addr + STATUS))) {235writel(host->io_word[0], host->addr + DATA);236host->io_pos = 0;237host->io_word[0] = 0;238} else if (host->io_pos) {239return off;240}241242if (!length)243return off;244245while (!(STATUS_FIFO_FULL & readl(host->addr + STATUS))) {246if (length < 4)247break;248249__raw_writel(*(unsigned int *)(buf + off),250host->addr + DATA);251length -= 4;252off += 4;253}254255switch (length) {256case 3:257host->io_word[0] |= buf[off + 2] << 16;258host->io_pos++;259case 2:260host->io_word[0] |= buf[off + 1] << 8;261host->io_pos++;262case 1:263host->io_word[0] |= buf[off];264host->io_pos++;265}266267off += host->io_pos;268269return off;270}271272static unsigned int jmb38x_ms_write_reg_data(struct jmb38x_ms_host *host,273unsigned char *buf,274unsigned int length)275{276unsigned int off = 0;277278while (host->io_pos < 4 && length) {279host->io_word[0] &= ~(0xff << (host->io_pos * 8));280host->io_word[0] |= buf[off++] << (host->io_pos * 8);281host->io_pos++;282length--;283}284285if (!length)286return off;287288while (host->io_pos < 8 && length) {289host->io_word[1] &= ~(0xff << (host->io_pos * 8));290host->io_word[1] |= buf[off++] << (host->io_pos * 8);291host->io_pos++;292length--;293}294295return off;296}297298static int jmb38x_ms_transfer_data(struct jmb38x_ms_host *host)299{300unsigned int length;301unsigned int off;302unsigned int t_size, p_cnt;303unsigned char *buf;304struct page *pg;305unsigned long flags = 0;306307if (host->req->long_data) {308length = host->req->sg.length - host->block_pos;309off = host->req->sg.offset + host->block_pos;310} else {311length = host->req->data_len - host->block_pos;312off = 0;313}314315while (length) {316unsigned int uninitialized_var(p_off);317318if (host->req->long_data) {319pg = nth_page(sg_page(&host->req->sg),320off >> PAGE_SHIFT);321p_off = offset_in_page(off);322p_cnt = PAGE_SIZE - p_off;323p_cnt = min(p_cnt, length);324325local_irq_save(flags);326buf = kmap_atomic(pg, KM_BIO_SRC_IRQ) + p_off;327} else {328buf = host->req->data + host->block_pos;329p_cnt = host->req->data_len - host->block_pos;330}331332if (host->req->data_dir == WRITE)333t_size = !(host->cmd_flags & REG_DATA)334? jmb38x_ms_write_data(host, buf, p_cnt)335: jmb38x_ms_write_reg_data(host, buf, p_cnt);336else337t_size = !(host->cmd_flags & REG_DATA)338? jmb38x_ms_read_data(host, buf, p_cnt)339: jmb38x_ms_read_reg_data(host, buf, p_cnt);340341if (host->req->long_data) {342kunmap_atomic(buf - p_off, KM_BIO_SRC_IRQ);343local_irq_restore(flags);344}345346if (!t_size)347break;348host->block_pos += t_size;349length -= t_size;350off += t_size;351}352353if (!length && host->req->data_dir == WRITE) {354if (host->cmd_flags & REG_DATA) {355writel(host->io_word[0], host->addr + TPC_P0);356writel(host->io_word[1], host->addr + TPC_P1);357} else if (host->io_pos) {358writel(host->io_word[0], host->addr + DATA);359}360}361362return length;363}364365static int jmb38x_ms_issue_cmd(struct memstick_host *msh)366{367struct jmb38x_ms_host *host = memstick_priv(msh);368unsigned char *data;369unsigned int data_len, cmd, t_val;370371if (!(STATUS_HAS_MEDIA & readl(host->addr + STATUS))) {372dev_dbg(&msh->dev, "no media status\n");373host->req->error = -ETIME;374return host->req->error;375}376377dev_dbg(&msh->dev, "control %08x\n", readl(host->addr + HOST_CONTROL));378dev_dbg(&msh->dev, "status %08x\n", readl(host->addr + INT_STATUS));379dev_dbg(&msh->dev, "hstatus %08x\n", readl(host->addr + STATUS));380381host->cmd_flags = 0;382host->block_pos = 0;383host->io_pos = 0;384host->io_word[0] = 0;385host->io_word[1] = 0;386387cmd = host->req->tpc << 16;388cmd |= TPC_DATA_SEL;389390if (host->req->data_dir == READ)391cmd |= TPC_DIR;392393if (host->req->need_card_int) {394if (host->ifmode == MEMSTICK_SERIAL)395cmd |= TPC_GET_INT;396else397cmd |= TPC_WAIT_INT;398}399400data = host->req->data;401402if (!no_dma)403host->cmd_flags |= DMA_DATA;404405if (host->req->long_data) {406data_len = host->req->sg.length;407} else {408data_len = host->req->data_len;409host->cmd_flags &= ~DMA_DATA;410}411412if (data_len <= 8) {413cmd &= ~(TPC_DATA_SEL | 0xf);414host->cmd_flags |= REG_DATA;415cmd |= data_len & 0xf;416host->cmd_flags &= ~DMA_DATA;417}418419if (host->cmd_flags & DMA_DATA) {420if (1 != pci_map_sg(host->chip->pdev, &host->req->sg, 1,421host->req->data_dir == READ422? PCI_DMA_FROMDEVICE423: PCI_DMA_TODEVICE)) {424host->req->error = -ENOMEM;425return host->req->error;426}427data_len = sg_dma_len(&host->req->sg);428writel(sg_dma_address(&host->req->sg),429host->addr + DMA_ADDRESS);430writel(((1 << 16) & BLOCK_COUNT_MASK)431| (data_len & BLOCK_SIZE_MASK),432host->addr + BLOCK);433writel(DMA_CONTROL_ENABLE, host->addr + DMA_CONTROL);434} else if (!(host->cmd_flags & REG_DATA)) {435writel(((1 << 16) & BLOCK_COUNT_MASK)436| (data_len & BLOCK_SIZE_MASK),437host->addr + BLOCK);438t_val = readl(host->addr + INT_STATUS_ENABLE);439t_val |= host->req->data_dir == READ440? INT_STATUS_FIFO_RRDY441: INT_STATUS_FIFO_WRDY;442443writel(t_val, host->addr + INT_STATUS_ENABLE);444writel(t_val, host->addr + INT_SIGNAL_ENABLE);445} else {446cmd &= ~(TPC_DATA_SEL | 0xf);447host->cmd_flags |= REG_DATA;448cmd |= data_len & 0xf;449450if (host->req->data_dir == WRITE) {451jmb38x_ms_transfer_data(host);452writel(host->io_word[0], host->addr + TPC_P0);453writel(host->io_word[1], host->addr + TPC_P1);454}455}456457mod_timer(&host->timer, jiffies + host->timeout_jiffies);458writel(HOST_CONTROL_LED | readl(host->addr + HOST_CONTROL),459host->addr + HOST_CONTROL);460host->req->error = 0;461462writel(cmd, host->addr + TPC);463dev_dbg(&msh->dev, "executing TPC %08x, len %x\n", cmd, data_len);464465return 0;466}467468static void jmb38x_ms_complete_cmd(struct memstick_host *msh, int last)469{470struct jmb38x_ms_host *host = memstick_priv(msh);471unsigned int t_val = 0;472int rc;473474del_timer(&host->timer);475476dev_dbg(&msh->dev, "c control %08x\n",477readl(host->addr + HOST_CONTROL));478dev_dbg(&msh->dev, "c status %08x\n",479readl(host->addr + INT_STATUS));480dev_dbg(&msh->dev, "c hstatus %08x\n", readl(host->addr + STATUS));481482host->req->int_reg = readl(host->addr + STATUS) & 0xff;483484writel(0, host->addr + BLOCK);485writel(0, host->addr + DMA_CONTROL);486487if (host->cmd_flags & DMA_DATA) {488pci_unmap_sg(host->chip->pdev, &host->req->sg, 1,489host->req->data_dir == READ490? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);491} else {492t_val = readl(host->addr + INT_STATUS_ENABLE);493if (host->req->data_dir == READ)494t_val &= ~INT_STATUS_FIFO_RRDY;495else496t_val &= ~INT_STATUS_FIFO_WRDY;497498writel(t_val, host->addr + INT_STATUS_ENABLE);499writel(t_val, host->addr + INT_SIGNAL_ENABLE);500}501502writel((~HOST_CONTROL_LED) & readl(host->addr + HOST_CONTROL),503host->addr + HOST_CONTROL);504505if (!last) {506do {507rc = memstick_next_req(msh, &host->req);508} while (!rc && jmb38x_ms_issue_cmd(msh));509} else {510do {511rc = memstick_next_req(msh, &host->req);512if (!rc)513host->req->error = -ETIME;514} while (!rc);515}516}517518static irqreturn_t jmb38x_ms_isr(int irq, void *dev_id)519{520struct memstick_host *msh = dev_id;521struct jmb38x_ms_host *host = memstick_priv(msh);522unsigned int irq_status;523524spin_lock(&host->lock);525irq_status = readl(host->addr + INT_STATUS);526dev_dbg(&host->chip->pdev->dev, "irq_status = %08x\n", irq_status);527if (irq_status == 0 || irq_status == (~0)) {528spin_unlock(&host->lock);529return IRQ_NONE;530}531532if (host->req) {533if (irq_status & INT_STATUS_ANY_ERR) {534if (irq_status & INT_STATUS_CRC_ERR)535host->req->error = -EILSEQ;536else if (irq_status & INT_STATUS_TPC_ERR) {537dev_dbg(&host->chip->pdev->dev, "TPC_ERR\n");538jmb38x_ms_complete_cmd(msh, 0);539} else540host->req->error = -ETIME;541} else {542if (host->cmd_flags & DMA_DATA) {543if (irq_status & INT_STATUS_EOTRAN)544host->cmd_flags |= FIFO_READY;545} else {546if (irq_status & (INT_STATUS_FIFO_RRDY547| INT_STATUS_FIFO_WRDY))548jmb38x_ms_transfer_data(host);549550if (irq_status & INT_STATUS_EOTRAN) {551jmb38x_ms_transfer_data(host);552host->cmd_flags |= FIFO_READY;553}554}555556if (irq_status & INT_STATUS_EOTPC) {557host->cmd_flags |= CMD_READY;558if (host->cmd_flags & REG_DATA) {559if (host->req->data_dir == READ) {560host->io_word[0]561= readl(host->addr562+ TPC_P0);563host->io_word[1]564= readl(host->addr565+ TPC_P1);566host->io_pos = 8;567568jmb38x_ms_transfer_data(host);569}570host->cmd_flags |= FIFO_READY;571}572}573}574}575576if (irq_status & (INT_STATUS_MEDIA_IN | INT_STATUS_MEDIA_OUT)) {577dev_dbg(&host->chip->pdev->dev, "media changed\n");578memstick_detect_change(msh);579}580581writel(irq_status, host->addr + INT_STATUS);582583if (host->req584&& (((host->cmd_flags & CMD_READY)585&& (host->cmd_flags & FIFO_READY))586|| host->req->error))587jmb38x_ms_complete_cmd(msh, 0);588589spin_unlock(&host->lock);590return IRQ_HANDLED;591}592593static void jmb38x_ms_abort(unsigned long data)594{595struct memstick_host *msh = (struct memstick_host *)data;596struct jmb38x_ms_host *host = memstick_priv(msh);597unsigned long flags;598599dev_dbg(&host->chip->pdev->dev, "abort\n");600spin_lock_irqsave(&host->lock, flags);601if (host->req) {602host->req->error = -ETIME;603jmb38x_ms_complete_cmd(msh, 0);604}605spin_unlock_irqrestore(&host->lock, flags);606}607608static void jmb38x_ms_req_tasklet(unsigned long data)609{610struct memstick_host *msh = (struct memstick_host *)data;611struct jmb38x_ms_host *host = memstick_priv(msh);612unsigned long flags;613int rc;614615spin_lock_irqsave(&host->lock, flags);616if (!host->req) {617do {618rc = memstick_next_req(msh, &host->req);619dev_dbg(&host->chip->pdev->dev, "tasklet req %d\n", rc);620} while (!rc && jmb38x_ms_issue_cmd(msh));621}622spin_unlock_irqrestore(&host->lock, flags);623}624625static void jmb38x_ms_dummy_submit(struct memstick_host *msh)626{627return;628}629630static void jmb38x_ms_submit_req(struct memstick_host *msh)631{632struct jmb38x_ms_host *host = memstick_priv(msh);633634tasklet_schedule(&host->notify);635}636637static int jmb38x_ms_reset(struct jmb38x_ms_host *host)638{639int cnt;640641writel(HOST_CONTROL_RESET_REQ | HOST_CONTROL_CLOCK_EN642| readl(host->addr + HOST_CONTROL),643host->addr + HOST_CONTROL);644mmiowb();645646for (cnt = 0; cnt < 20; ++cnt) {647if (!(HOST_CONTROL_RESET_REQ648& readl(host->addr + HOST_CONTROL)))649goto reset_next;650651ndelay(20);652}653dev_dbg(&host->chip->pdev->dev, "reset_req timeout\n");654655reset_next:656writel(HOST_CONTROL_RESET | HOST_CONTROL_CLOCK_EN657| readl(host->addr + HOST_CONTROL),658host->addr + HOST_CONTROL);659mmiowb();660661for (cnt = 0; cnt < 20; ++cnt) {662if (!(HOST_CONTROL_RESET663& readl(host->addr + HOST_CONTROL)))664goto reset_ok;665666ndelay(20);667}668dev_dbg(&host->chip->pdev->dev, "reset timeout\n");669return -EIO;670671reset_ok:672mmiowb();673writel(INT_STATUS_ALL, host->addr + INT_SIGNAL_ENABLE);674writel(INT_STATUS_ALL, host->addr + INT_STATUS_ENABLE);675return 0;676}677678static int jmb38x_ms_set_param(struct memstick_host *msh,679enum memstick_param param,680int value)681{682struct jmb38x_ms_host *host = memstick_priv(msh);683unsigned int host_ctl = readl(host->addr + HOST_CONTROL);684unsigned int clock_ctl = CLOCK_CONTROL_BY_MMIO, clock_delay = 0;685int rc = 0;686687switch (param) {688case MEMSTICK_POWER:689if (value == MEMSTICK_POWER_ON) {690rc = jmb38x_ms_reset(host);691if (rc)692return rc;693694host_ctl = 7;695host_ctl |= HOST_CONTROL_POWER_EN696| HOST_CONTROL_CLOCK_EN;697writel(host_ctl, host->addr + HOST_CONTROL);698699writel(host->id ? PAD_PU_PD_ON_MS_SOCK1700: PAD_PU_PD_ON_MS_SOCK0,701host->addr + PAD_PU_PD);702703writel(PAD_OUTPUT_ENABLE_MS,704host->addr + PAD_OUTPUT_ENABLE);705706msleep(10);707dev_dbg(&host->chip->pdev->dev, "power on\n");708} else if (value == MEMSTICK_POWER_OFF) {709host_ctl &= ~(HOST_CONTROL_POWER_EN710| HOST_CONTROL_CLOCK_EN);711writel(host_ctl, host->addr + HOST_CONTROL);712writel(0, host->addr + PAD_OUTPUT_ENABLE);713writel(PAD_PU_PD_OFF, host->addr + PAD_PU_PD);714dev_dbg(&host->chip->pdev->dev, "power off\n");715} else716return -EINVAL;717break;718case MEMSTICK_INTERFACE:719dev_dbg(&host->chip->pdev->dev,720"Set Host Interface Mode to %d\n", value);721host_ctl &= ~(HOST_CONTROL_FAST_CLK | HOST_CONTROL_REI |722HOST_CONTROL_REO);723host_ctl |= HOST_CONTROL_TDELAY_EN | HOST_CONTROL_HW_OC_P;724host_ctl &= ~(3 << HOST_CONTROL_IF_SHIFT);725726if (value == MEMSTICK_SERIAL) {727host_ctl |= HOST_CONTROL_IF_SERIAL728<< HOST_CONTROL_IF_SHIFT;729host_ctl |= HOST_CONTROL_REI;730clock_ctl |= CLOCK_CONTROL_40MHZ;731clock_delay = 0;732} else if (value == MEMSTICK_PAR4) {733host_ctl |= HOST_CONTROL_FAST_CLK;734host_ctl |= HOST_CONTROL_IF_PAR4735<< HOST_CONTROL_IF_SHIFT;736host_ctl |= HOST_CONTROL_REO;737clock_ctl |= CLOCK_CONTROL_40MHZ;738clock_delay = 4;739} else if (value == MEMSTICK_PAR8) {740host_ctl |= HOST_CONTROL_FAST_CLK;741host_ctl |= HOST_CONTROL_IF_PAR8742<< HOST_CONTROL_IF_SHIFT;743clock_ctl |= CLOCK_CONTROL_50MHZ;744clock_delay = 0;745} else746return -EINVAL;747748writel(host_ctl, host->addr + HOST_CONTROL);749writel(CLOCK_CONTROL_OFF, host->addr + CLOCK_CONTROL);750writel(clock_ctl, host->addr + CLOCK_CONTROL);751pci_write_config_byte(host->chip->pdev,752PCI_CTL_CLOCK_DLY_ADDR + 1,753clock_delay);754host->ifmode = value;755break;756};757return 0;758}759760#define PCI_PMOS0_CONTROL 0xae761#define PMOS0_ENABLE 0x01762#define PMOS0_OVERCURRENT_LEVEL_2_4V 0x06763#define PMOS0_EN_OVERCURRENT_DEBOUNCE 0x40764#define PMOS0_SW_LED_POLARITY_ENABLE 0x80765#define PMOS0_ACTIVE_BITS (PMOS0_ENABLE | PMOS0_EN_OVERCURRENT_DEBOUNCE | \766PMOS0_OVERCURRENT_LEVEL_2_4V)767#define PCI_PMOS1_CONTROL 0xbd768#define PMOS1_ACTIVE_BITS 0x4a769#define PCI_CLOCK_CTL 0xb9770771static int jmb38x_ms_pmos(struct pci_dev *pdev, int flag)772{773unsigned char val;774775pci_read_config_byte(pdev, PCI_PMOS0_CONTROL, &val);776if (flag)777val |= PMOS0_ACTIVE_BITS;778else779val &= ~PMOS0_ACTIVE_BITS;780pci_write_config_byte(pdev, PCI_PMOS0_CONTROL, val);781dev_dbg(&pdev->dev, "JMB38x: set PMOS0 val 0x%x\n", val);782783if (pci_resource_flags(pdev, 1)) {784pci_read_config_byte(pdev, PCI_PMOS1_CONTROL, &val);785if (flag)786val |= PMOS1_ACTIVE_BITS;787else788val &= ~PMOS1_ACTIVE_BITS;789pci_write_config_byte(pdev, PCI_PMOS1_CONTROL, val);790dev_dbg(&pdev->dev, "JMB38x: set PMOS1 val 0x%x\n", val);791}792793pci_read_config_byte(pdev, PCI_CLOCK_CTL, &val);794pci_write_config_byte(pdev, PCI_CLOCK_CTL, val & ~0x0f);795pci_write_config_byte(pdev, PCI_CLOCK_CTL, val | 0x01);796dev_dbg(&pdev->dev, "Clock Control by PCI config is disabled!\n");797798return 0;799}800801#ifdef CONFIG_PM802803static int jmb38x_ms_suspend(struct pci_dev *dev, pm_message_t state)804{805struct jmb38x_ms *jm = pci_get_drvdata(dev);806int cnt;807808for (cnt = 0; cnt < jm->host_cnt; ++cnt) {809if (!jm->hosts[cnt])810break;811memstick_suspend_host(jm->hosts[cnt]);812}813814pci_save_state(dev);815pci_enable_wake(dev, pci_choose_state(dev, state), 0);816pci_disable_device(dev);817pci_set_power_state(dev, pci_choose_state(dev, state));818return 0;819}820821static int jmb38x_ms_resume(struct pci_dev *dev)822{823struct jmb38x_ms *jm = pci_get_drvdata(dev);824int rc;825826pci_set_power_state(dev, PCI_D0);827pci_restore_state(dev);828rc = pci_enable_device(dev);829if (rc)830return rc;831pci_set_master(dev);832833jmb38x_ms_pmos(dev, 1);834835for (rc = 0; rc < jm->host_cnt; ++rc) {836if (!jm->hosts[rc])837break;838memstick_resume_host(jm->hosts[rc]);839memstick_detect_change(jm->hosts[rc]);840}841842return 0;843}844845#else846847#define jmb38x_ms_suspend NULL848#define jmb38x_ms_resume NULL849850#endif /* CONFIG_PM */851852static int jmb38x_ms_count_slots(struct pci_dev *pdev)853{854int cnt, rc = 0;855856for (cnt = 0; cnt < PCI_ROM_RESOURCE; ++cnt) {857if (!(IORESOURCE_MEM & pci_resource_flags(pdev, cnt)))858break;859860if (256 != pci_resource_len(pdev, cnt))861break;862863++rc;864}865return rc;866}867868static struct memstick_host *jmb38x_ms_alloc_host(struct jmb38x_ms *jm, int cnt)869{870struct memstick_host *msh;871struct jmb38x_ms_host *host;872873msh = memstick_alloc_host(sizeof(struct jmb38x_ms_host),874&jm->pdev->dev);875if (!msh)876return NULL;877878host = memstick_priv(msh);879host->chip = jm;880host->addr = ioremap(pci_resource_start(jm->pdev, cnt),881pci_resource_len(jm->pdev, cnt));882if (!host->addr)883goto err_out_free;884885spin_lock_init(&host->lock);886host->id = cnt;887snprintf(host->host_id, sizeof(host->host_id), DRIVER_NAME ":slot%d",888host->id);889host->irq = jm->pdev->irq;890host->timeout_jiffies = msecs_to_jiffies(1000);891892tasklet_init(&host->notify, jmb38x_ms_req_tasklet, (unsigned long)msh);893msh->request = jmb38x_ms_submit_req;894msh->set_param = jmb38x_ms_set_param;895896msh->caps = MEMSTICK_CAP_PAR4 | MEMSTICK_CAP_PAR8;897898setup_timer(&host->timer, jmb38x_ms_abort, (unsigned long)msh);899900if (!request_irq(host->irq, jmb38x_ms_isr, IRQF_SHARED, host->host_id,901msh))902return msh;903904iounmap(host->addr);905err_out_free:906kfree(msh);907return NULL;908}909910static void jmb38x_ms_free_host(struct memstick_host *msh)911{912struct jmb38x_ms_host *host = memstick_priv(msh);913914free_irq(host->irq, msh);915iounmap(host->addr);916memstick_free_host(msh);917}918919static int jmb38x_ms_probe(struct pci_dev *pdev,920const struct pci_device_id *dev_id)921{922struct jmb38x_ms *jm;923int pci_dev_busy = 0;924int rc, cnt;925926rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));927if (rc)928return rc;929930rc = pci_enable_device(pdev);931if (rc)932return rc;933934pci_set_master(pdev);935936rc = pci_request_regions(pdev, DRIVER_NAME);937if (rc) {938pci_dev_busy = 1;939goto err_out;940}941942jmb38x_ms_pmos(pdev, 1);943944cnt = jmb38x_ms_count_slots(pdev);945if (!cnt) {946rc = -ENODEV;947pci_dev_busy = 1;948goto err_out;949}950951jm = kzalloc(sizeof(struct jmb38x_ms)952+ cnt * sizeof(struct memstick_host *), GFP_KERNEL);953if (!jm) {954rc = -ENOMEM;955goto err_out_int;956}957958jm->pdev = pdev;959jm->host_cnt = cnt;960pci_set_drvdata(pdev, jm);961962for (cnt = 0; cnt < jm->host_cnt; ++cnt) {963jm->hosts[cnt] = jmb38x_ms_alloc_host(jm, cnt);964if (!jm->hosts[cnt])965break;966967rc = memstick_add_host(jm->hosts[cnt]);968969if (rc) {970jmb38x_ms_free_host(jm->hosts[cnt]);971jm->hosts[cnt] = NULL;972break;973}974}975976if (cnt)977return 0;978979rc = -ENODEV;980981pci_set_drvdata(pdev, NULL);982kfree(jm);983err_out_int:984pci_release_regions(pdev);985err_out:986if (!pci_dev_busy)987pci_disable_device(pdev);988return rc;989}990991static void jmb38x_ms_remove(struct pci_dev *dev)992{993struct jmb38x_ms *jm = pci_get_drvdata(dev);994struct jmb38x_ms_host *host;995int cnt;996unsigned long flags;997998for (cnt = 0; cnt < jm->host_cnt; ++cnt) {999if (!jm->hosts[cnt])1000break;10011002host = memstick_priv(jm->hosts[cnt]);10031004jm->hosts[cnt]->request = jmb38x_ms_dummy_submit;1005tasklet_kill(&host->notify);1006writel(0, host->addr + INT_SIGNAL_ENABLE);1007writel(0, host->addr + INT_STATUS_ENABLE);1008mmiowb();1009dev_dbg(&jm->pdev->dev, "interrupts off\n");1010spin_lock_irqsave(&host->lock, flags);1011if (host->req) {1012host->req->error = -ETIME;1013jmb38x_ms_complete_cmd(jm->hosts[cnt], 1);1014}1015spin_unlock_irqrestore(&host->lock, flags);10161017memstick_remove_host(jm->hosts[cnt]);1018dev_dbg(&jm->pdev->dev, "host removed\n");10191020jmb38x_ms_free_host(jm->hosts[cnt]);1021}10221023jmb38x_ms_pmos(dev, 0);10241025pci_set_drvdata(dev, NULL);1026pci_release_regions(dev);1027pci_disable_device(dev);1028kfree(jm);1029}10301031static struct pci_device_id jmb38x_ms_id_tbl [] = {1032{ PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB38X_MS) },1033{ PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB385_MS) },1034{ PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB390_MS) },1035{ }1036};10371038static struct pci_driver jmb38x_ms_driver = {1039.name = DRIVER_NAME,1040.id_table = jmb38x_ms_id_tbl,1041.probe = jmb38x_ms_probe,1042.remove = jmb38x_ms_remove,1043.suspend = jmb38x_ms_suspend,1044.resume = jmb38x_ms_resume1045};10461047static int __init jmb38x_ms_init(void)1048{1049return pci_register_driver(&jmb38x_ms_driver);1050}10511052static void __exit jmb38x_ms_exit(void)1053{1054pci_unregister_driver(&jmb38x_ms_driver);1055}10561057MODULE_AUTHOR("Alex Dubov");1058MODULE_DESCRIPTION("JMicron jmb38x MemoryStick driver");1059MODULE_LICENSE("GPL");1060MODULE_DEVICE_TABLE(pci, jmb38x_ms_id_tbl);10611062module_init(jmb38x_ms_init);1063module_exit(jmb38x_ms_exit);106410651066