162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * jmb38x_ms.c - JMicron jmb38x MemoryStick card reader 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2008 Alex Dubov <oakad@yahoo.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/spinlock.h> 962306a36Sopenharmony_ci#include <linux/interrupt.h> 1062306a36Sopenharmony_ci#include <linux/pci.h> 1162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/highmem.h> 1462306a36Sopenharmony_ci#include <linux/memstick.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define DRIVER_NAME "jmb38x_ms" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic bool no_dma; 2162306a36Sopenharmony_cimodule_param(no_dma, bool, 0644); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cienum { 2462306a36Sopenharmony_ci DMA_ADDRESS = 0x00, 2562306a36Sopenharmony_ci BLOCK = 0x04, 2662306a36Sopenharmony_ci DMA_CONTROL = 0x08, 2762306a36Sopenharmony_ci TPC_P0 = 0x0c, 2862306a36Sopenharmony_ci TPC_P1 = 0x10, 2962306a36Sopenharmony_ci TPC = 0x14, 3062306a36Sopenharmony_ci HOST_CONTROL = 0x18, 3162306a36Sopenharmony_ci DATA = 0x1c, 3262306a36Sopenharmony_ci STATUS = 0x20, 3362306a36Sopenharmony_ci INT_STATUS = 0x24, 3462306a36Sopenharmony_ci INT_STATUS_ENABLE = 0x28, 3562306a36Sopenharmony_ci INT_SIGNAL_ENABLE = 0x2c, 3662306a36Sopenharmony_ci TIMER = 0x30, 3762306a36Sopenharmony_ci TIMER_CONTROL = 0x34, 3862306a36Sopenharmony_ci PAD_OUTPUT_ENABLE = 0x38, 3962306a36Sopenharmony_ci PAD_PU_PD = 0x3c, 4062306a36Sopenharmony_ci CLOCK_DELAY = 0x40, 4162306a36Sopenharmony_ci ADMA_ADDRESS = 0x44, 4262306a36Sopenharmony_ci CLOCK_CONTROL = 0x48, 4362306a36Sopenharmony_ci LED_CONTROL = 0x4c, 4462306a36Sopenharmony_ci VERSION = 0x50 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistruct jmb38x_ms_host { 4862306a36Sopenharmony_ci struct jmb38x_ms *chip; 4962306a36Sopenharmony_ci void __iomem *addr; 5062306a36Sopenharmony_ci spinlock_t lock; 5162306a36Sopenharmony_ci struct tasklet_struct notify; 5262306a36Sopenharmony_ci int id; 5362306a36Sopenharmony_ci char host_id[32]; 5462306a36Sopenharmony_ci int irq; 5562306a36Sopenharmony_ci unsigned int block_pos; 5662306a36Sopenharmony_ci unsigned long timeout_jiffies; 5762306a36Sopenharmony_ci struct timer_list timer; 5862306a36Sopenharmony_ci struct memstick_host *msh; 5962306a36Sopenharmony_ci struct memstick_request *req; 6062306a36Sopenharmony_ci unsigned char cmd_flags; 6162306a36Sopenharmony_ci unsigned char io_pos; 6262306a36Sopenharmony_ci unsigned char ifmode; 6362306a36Sopenharmony_ci unsigned int io_word[2]; 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistruct jmb38x_ms { 6762306a36Sopenharmony_ci struct pci_dev *pdev; 6862306a36Sopenharmony_ci int host_cnt; 6962306a36Sopenharmony_ci struct memstick_host *hosts[]; 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define BLOCK_COUNT_MASK 0xffff0000 7362306a36Sopenharmony_ci#define BLOCK_SIZE_MASK 0x00000fff 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci#define DMA_CONTROL_ENABLE 0x00000001 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define TPC_DATA_SEL 0x00008000 7862306a36Sopenharmony_ci#define TPC_DIR 0x00004000 7962306a36Sopenharmony_ci#define TPC_WAIT_INT 0x00002000 8062306a36Sopenharmony_ci#define TPC_GET_INT 0x00000800 8162306a36Sopenharmony_ci#define TPC_CODE_SZ_MASK 0x00000700 8262306a36Sopenharmony_ci#define TPC_DATA_SZ_MASK 0x00000007 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define HOST_CONTROL_TDELAY_EN 0x00040000 8562306a36Sopenharmony_ci#define HOST_CONTROL_HW_OC_P 0x00010000 8662306a36Sopenharmony_ci#define HOST_CONTROL_RESET_REQ 0x00008000 8762306a36Sopenharmony_ci#define HOST_CONTROL_REI 0x00004000 8862306a36Sopenharmony_ci#define HOST_CONTROL_LED 0x00000400 8962306a36Sopenharmony_ci#define HOST_CONTROL_FAST_CLK 0x00000200 9062306a36Sopenharmony_ci#define HOST_CONTROL_RESET 0x00000100 9162306a36Sopenharmony_ci#define HOST_CONTROL_POWER_EN 0x00000080 9262306a36Sopenharmony_ci#define HOST_CONTROL_CLOCK_EN 0x00000040 9362306a36Sopenharmony_ci#define HOST_CONTROL_REO 0x00000008 9462306a36Sopenharmony_ci#define HOST_CONTROL_IF_SHIFT 4 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define HOST_CONTROL_IF_SERIAL 0x0 9762306a36Sopenharmony_ci#define HOST_CONTROL_IF_PAR4 0x1 9862306a36Sopenharmony_ci#define HOST_CONTROL_IF_PAR8 0x3 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define STATUS_BUSY 0x00080000 10162306a36Sopenharmony_ci#define STATUS_MS_DAT7 0x00040000 10262306a36Sopenharmony_ci#define STATUS_MS_DAT6 0x00020000 10362306a36Sopenharmony_ci#define STATUS_MS_DAT5 0x00010000 10462306a36Sopenharmony_ci#define STATUS_MS_DAT4 0x00008000 10562306a36Sopenharmony_ci#define STATUS_MS_DAT3 0x00004000 10662306a36Sopenharmony_ci#define STATUS_MS_DAT2 0x00002000 10762306a36Sopenharmony_ci#define STATUS_MS_DAT1 0x00001000 10862306a36Sopenharmony_ci#define STATUS_MS_DAT0 0x00000800 10962306a36Sopenharmony_ci#define STATUS_HAS_MEDIA 0x00000400 11062306a36Sopenharmony_ci#define STATUS_FIFO_EMPTY 0x00000200 11162306a36Sopenharmony_ci#define STATUS_FIFO_FULL 0x00000100 11262306a36Sopenharmony_ci#define STATUS_MS_CED 0x00000080 11362306a36Sopenharmony_ci#define STATUS_MS_ERR 0x00000040 11462306a36Sopenharmony_ci#define STATUS_MS_BRQ 0x00000020 11562306a36Sopenharmony_ci#define STATUS_MS_CNK 0x00000001 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#define INT_STATUS_TPC_ERR 0x00080000 11862306a36Sopenharmony_ci#define INT_STATUS_CRC_ERR 0x00040000 11962306a36Sopenharmony_ci#define INT_STATUS_TIMER_TO 0x00020000 12062306a36Sopenharmony_ci#define INT_STATUS_HSK_TO 0x00010000 12162306a36Sopenharmony_ci#define INT_STATUS_ANY_ERR 0x00008000 12262306a36Sopenharmony_ci#define INT_STATUS_FIFO_WRDY 0x00000080 12362306a36Sopenharmony_ci#define INT_STATUS_FIFO_RRDY 0x00000040 12462306a36Sopenharmony_ci#define INT_STATUS_MEDIA_OUT 0x00000010 12562306a36Sopenharmony_ci#define INT_STATUS_MEDIA_IN 0x00000008 12662306a36Sopenharmony_ci#define INT_STATUS_DMA_BOUNDARY 0x00000004 12762306a36Sopenharmony_ci#define INT_STATUS_EOTRAN 0x00000002 12862306a36Sopenharmony_ci#define INT_STATUS_EOTPC 0x00000001 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci#define INT_STATUS_ALL 0x000f801f 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci#define PAD_OUTPUT_ENABLE_MS 0x0F3F 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci#define PAD_PU_PD_OFF 0x7FFF0000 13562306a36Sopenharmony_ci#define PAD_PU_PD_ON_MS_SOCK0 0x5f8f0000 13662306a36Sopenharmony_ci#define PAD_PU_PD_ON_MS_SOCK1 0x0f0f0000 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci#define CLOCK_CONTROL_BY_MMIO 0x00000008 13962306a36Sopenharmony_ci#define CLOCK_CONTROL_40MHZ 0x00000001 14062306a36Sopenharmony_ci#define CLOCK_CONTROL_50MHZ 0x00000002 14162306a36Sopenharmony_ci#define CLOCK_CONTROL_60MHZ 0x00000010 14262306a36Sopenharmony_ci#define CLOCK_CONTROL_62_5MHZ 0x00000004 14362306a36Sopenharmony_ci#define CLOCK_CONTROL_OFF 0x00000000 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci#define PCI_CTL_CLOCK_DLY_ADDR 0x000000b0 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cienum { 14862306a36Sopenharmony_ci CMD_READY = 0x01, 14962306a36Sopenharmony_ci FIFO_READY = 0x02, 15062306a36Sopenharmony_ci REG_DATA = 0x04, 15162306a36Sopenharmony_ci DMA_DATA = 0x08 15262306a36Sopenharmony_ci}; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic unsigned int jmb38x_ms_read_data(struct jmb38x_ms_host *host, 15562306a36Sopenharmony_ci unsigned char *buf, unsigned int length) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci unsigned int off = 0; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci while (host->io_pos && length) { 16062306a36Sopenharmony_ci buf[off++] = host->io_word[0] & 0xff; 16162306a36Sopenharmony_ci host->io_word[0] >>= 8; 16262306a36Sopenharmony_ci length--; 16362306a36Sopenharmony_ci host->io_pos--; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (!length) 16762306a36Sopenharmony_ci return off; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci while (!(STATUS_FIFO_EMPTY & readl(host->addr + STATUS))) { 17062306a36Sopenharmony_ci if (length < 4) 17162306a36Sopenharmony_ci break; 17262306a36Sopenharmony_ci *(unsigned int *)(buf + off) = __raw_readl(host->addr + DATA); 17362306a36Sopenharmony_ci length -= 4; 17462306a36Sopenharmony_ci off += 4; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (length 17862306a36Sopenharmony_ci && !(STATUS_FIFO_EMPTY & readl(host->addr + STATUS))) { 17962306a36Sopenharmony_ci host->io_word[0] = readl(host->addr + DATA); 18062306a36Sopenharmony_ci for (host->io_pos = 4; host->io_pos; --host->io_pos) { 18162306a36Sopenharmony_ci buf[off++] = host->io_word[0] & 0xff; 18262306a36Sopenharmony_ci host->io_word[0] >>= 8; 18362306a36Sopenharmony_ci length--; 18462306a36Sopenharmony_ci if (!length) 18562306a36Sopenharmony_ci break; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return off; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic unsigned int jmb38x_ms_read_reg_data(struct jmb38x_ms_host *host, 19362306a36Sopenharmony_ci unsigned char *buf, 19462306a36Sopenharmony_ci unsigned int length) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci unsigned int off = 0; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci while (host->io_pos > 4 && length) { 19962306a36Sopenharmony_ci buf[off++] = host->io_word[0] & 0xff; 20062306a36Sopenharmony_ci host->io_word[0] >>= 8; 20162306a36Sopenharmony_ci length--; 20262306a36Sopenharmony_ci host->io_pos--; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (!length) 20662306a36Sopenharmony_ci return off; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci while (host->io_pos && length) { 20962306a36Sopenharmony_ci buf[off++] = host->io_word[1] & 0xff; 21062306a36Sopenharmony_ci host->io_word[1] >>= 8; 21162306a36Sopenharmony_ci length--; 21262306a36Sopenharmony_ci host->io_pos--; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return off; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic unsigned int jmb38x_ms_write_data(struct jmb38x_ms_host *host, 21962306a36Sopenharmony_ci unsigned char *buf, 22062306a36Sopenharmony_ci unsigned int length) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci unsigned int off = 0; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (host->io_pos) { 22562306a36Sopenharmony_ci while (host->io_pos < 4 && length) { 22662306a36Sopenharmony_ci host->io_word[0] |= buf[off++] << (host->io_pos * 8); 22762306a36Sopenharmony_ci host->io_pos++; 22862306a36Sopenharmony_ci length--; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (host->io_pos == 4 23362306a36Sopenharmony_ci && !(STATUS_FIFO_FULL & readl(host->addr + STATUS))) { 23462306a36Sopenharmony_ci writel(host->io_word[0], host->addr + DATA); 23562306a36Sopenharmony_ci host->io_pos = 0; 23662306a36Sopenharmony_ci host->io_word[0] = 0; 23762306a36Sopenharmony_ci } else if (host->io_pos) { 23862306a36Sopenharmony_ci return off; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (!length) 24262306a36Sopenharmony_ci return off; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci while (!(STATUS_FIFO_FULL & readl(host->addr + STATUS))) { 24562306a36Sopenharmony_ci if (length < 4) 24662306a36Sopenharmony_ci break; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci __raw_writel(*(unsigned int *)(buf + off), 24962306a36Sopenharmony_ci host->addr + DATA); 25062306a36Sopenharmony_ci length -= 4; 25162306a36Sopenharmony_ci off += 4; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci switch (length) { 25562306a36Sopenharmony_ci case 3: 25662306a36Sopenharmony_ci host->io_word[0] |= buf[off + 2] << 16; 25762306a36Sopenharmony_ci host->io_pos++; 25862306a36Sopenharmony_ci fallthrough; 25962306a36Sopenharmony_ci case 2: 26062306a36Sopenharmony_ci host->io_word[0] |= buf[off + 1] << 8; 26162306a36Sopenharmony_ci host->io_pos++; 26262306a36Sopenharmony_ci fallthrough; 26362306a36Sopenharmony_ci case 1: 26462306a36Sopenharmony_ci host->io_word[0] |= buf[off]; 26562306a36Sopenharmony_ci host->io_pos++; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci off += host->io_pos; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return off; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic unsigned int jmb38x_ms_write_reg_data(struct jmb38x_ms_host *host, 27462306a36Sopenharmony_ci unsigned char *buf, 27562306a36Sopenharmony_ci unsigned int length) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci unsigned int off = 0; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci while (host->io_pos < 4 && length) { 28062306a36Sopenharmony_ci host->io_word[0] &= ~(0xff << (host->io_pos * 8)); 28162306a36Sopenharmony_ci host->io_word[0] |= buf[off++] << (host->io_pos * 8); 28262306a36Sopenharmony_ci host->io_pos++; 28362306a36Sopenharmony_ci length--; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (!length) 28762306a36Sopenharmony_ci return off; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci while (host->io_pos < 8 && length) { 29062306a36Sopenharmony_ci host->io_word[1] &= ~(0xff << (host->io_pos * 8)); 29162306a36Sopenharmony_ci host->io_word[1] |= buf[off++] << (host->io_pos * 8); 29262306a36Sopenharmony_ci host->io_pos++; 29362306a36Sopenharmony_ci length--; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci return off; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int jmb38x_ms_transfer_data(struct jmb38x_ms_host *host) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci unsigned int length; 30262306a36Sopenharmony_ci unsigned int off; 30362306a36Sopenharmony_ci unsigned int t_size, p_cnt; 30462306a36Sopenharmony_ci unsigned char *buf; 30562306a36Sopenharmony_ci struct page *pg; 30662306a36Sopenharmony_ci unsigned long flags = 0; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (host->req->long_data) { 30962306a36Sopenharmony_ci length = host->req->sg.length - host->block_pos; 31062306a36Sopenharmony_ci off = host->req->sg.offset + host->block_pos; 31162306a36Sopenharmony_ci } else { 31262306a36Sopenharmony_ci length = host->req->data_len - host->block_pos; 31362306a36Sopenharmony_ci off = 0; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci while (length) { 31762306a36Sopenharmony_ci unsigned int p_off; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (host->req->long_data) { 32062306a36Sopenharmony_ci pg = nth_page(sg_page(&host->req->sg), 32162306a36Sopenharmony_ci off >> PAGE_SHIFT); 32262306a36Sopenharmony_ci p_off = offset_in_page(off); 32362306a36Sopenharmony_ci p_cnt = PAGE_SIZE - p_off; 32462306a36Sopenharmony_ci p_cnt = min(p_cnt, length); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci local_irq_save(flags); 32762306a36Sopenharmony_ci buf = kmap_atomic(pg) + p_off; 32862306a36Sopenharmony_ci } else { 32962306a36Sopenharmony_ci buf = host->req->data + host->block_pos; 33062306a36Sopenharmony_ci p_cnt = host->req->data_len - host->block_pos; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (host->req->data_dir == WRITE) 33462306a36Sopenharmony_ci t_size = !(host->cmd_flags & REG_DATA) 33562306a36Sopenharmony_ci ? jmb38x_ms_write_data(host, buf, p_cnt) 33662306a36Sopenharmony_ci : jmb38x_ms_write_reg_data(host, buf, p_cnt); 33762306a36Sopenharmony_ci else 33862306a36Sopenharmony_ci t_size = !(host->cmd_flags & REG_DATA) 33962306a36Sopenharmony_ci ? jmb38x_ms_read_data(host, buf, p_cnt) 34062306a36Sopenharmony_ci : jmb38x_ms_read_reg_data(host, buf, p_cnt); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (host->req->long_data) { 34362306a36Sopenharmony_ci kunmap_atomic(buf - p_off); 34462306a36Sopenharmony_ci local_irq_restore(flags); 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (!t_size) 34862306a36Sopenharmony_ci break; 34962306a36Sopenharmony_ci host->block_pos += t_size; 35062306a36Sopenharmony_ci length -= t_size; 35162306a36Sopenharmony_ci off += t_size; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (!length && host->req->data_dir == WRITE) { 35562306a36Sopenharmony_ci if (host->cmd_flags & REG_DATA) { 35662306a36Sopenharmony_ci writel(host->io_word[0], host->addr + TPC_P0); 35762306a36Sopenharmony_ci writel(host->io_word[1], host->addr + TPC_P1); 35862306a36Sopenharmony_ci } else if (host->io_pos) { 35962306a36Sopenharmony_ci writel(host->io_word[0], host->addr + DATA); 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci return length; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic int jmb38x_ms_issue_cmd(struct memstick_host *msh) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci struct jmb38x_ms_host *host = memstick_priv(msh); 36962306a36Sopenharmony_ci unsigned int data_len, cmd, t_val; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (!(STATUS_HAS_MEDIA & readl(host->addr + STATUS))) { 37262306a36Sopenharmony_ci dev_dbg(&msh->dev, "no media status\n"); 37362306a36Sopenharmony_ci host->req->error = -ETIME; 37462306a36Sopenharmony_ci return host->req->error; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci dev_dbg(&msh->dev, "control %08x\n", readl(host->addr + HOST_CONTROL)); 37862306a36Sopenharmony_ci dev_dbg(&msh->dev, "status %08x\n", readl(host->addr + INT_STATUS)); 37962306a36Sopenharmony_ci dev_dbg(&msh->dev, "hstatus %08x\n", readl(host->addr + STATUS)); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci host->cmd_flags = 0; 38262306a36Sopenharmony_ci host->block_pos = 0; 38362306a36Sopenharmony_ci host->io_pos = 0; 38462306a36Sopenharmony_ci host->io_word[0] = 0; 38562306a36Sopenharmony_ci host->io_word[1] = 0; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci cmd = host->req->tpc << 16; 38862306a36Sopenharmony_ci cmd |= TPC_DATA_SEL; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (host->req->data_dir == READ) 39162306a36Sopenharmony_ci cmd |= TPC_DIR; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (host->req->need_card_int) { 39462306a36Sopenharmony_ci if (host->ifmode == MEMSTICK_SERIAL) 39562306a36Sopenharmony_ci cmd |= TPC_GET_INT; 39662306a36Sopenharmony_ci else 39762306a36Sopenharmony_ci cmd |= TPC_WAIT_INT; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (!no_dma) 40162306a36Sopenharmony_ci host->cmd_flags |= DMA_DATA; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (host->req->long_data) { 40462306a36Sopenharmony_ci data_len = host->req->sg.length; 40562306a36Sopenharmony_ci } else { 40662306a36Sopenharmony_ci data_len = host->req->data_len; 40762306a36Sopenharmony_ci host->cmd_flags &= ~DMA_DATA; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (data_len <= 8) { 41162306a36Sopenharmony_ci cmd &= ~(TPC_DATA_SEL | 0xf); 41262306a36Sopenharmony_ci host->cmd_flags |= REG_DATA; 41362306a36Sopenharmony_ci cmd |= data_len & 0xf; 41462306a36Sopenharmony_ci host->cmd_flags &= ~DMA_DATA; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (host->cmd_flags & DMA_DATA) { 41862306a36Sopenharmony_ci if (1 != dma_map_sg(&host->chip->pdev->dev, &host->req->sg, 1, 41962306a36Sopenharmony_ci host->req->data_dir == READ 42062306a36Sopenharmony_ci ? DMA_FROM_DEVICE 42162306a36Sopenharmony_ci : DMA_TO_DEVICE)) { 42262306a36Sopenharmony_ci host->req->error = -ENOMEM; 42362306a36Sopenharmony_ci return host->req->error; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci data_len = sg_dma_len(&host->req->sg); 42662306a36Sopenharmony_ci writel(sg_dma_address(&host->req->sg), 42762306a36Sopenharmony_ci host->addr + DMA_ADDRESS); 42862306a36Sopenharmony_ci writel(((1 << 16) & BLOCK_COUNT_MASK) 42962306a36Sopenharmony_ci | (data_len & BLOCK_SIZE_MASK), 43062306a36Sopenharmony_ci host->addr + BLOCK); 43162306a36Sopenharmony_ci writel(DMA_CONTROL_ENABLE, host->addr + DMA_CONTROL); 43262306a36Sopenharmony_ci } else if (!(host->cmd_flags & REG_DATA)) { 43362306a36Sopenharmony_ci writel(((1 << 16) & BLOCK_COUNT_MASK) 43462306a36Sopenharmony_ci | (data_len & BLOCK_SIZE_MASK), 43562306a36Sopenharmony_ci host->addr + BLOCK); 43662306a36Sopenharmony_ci t_val = readl(host->addr + INT_STATUS_ENABLE); 43762306a36Sopenharmony_ci t_val |= host->req->data_dir == READ 43862306a36Sopenharmony_ci ? INT_STATUS_FIFO_RRDY 43962306a36Sopenharmony_ci : INT_STATUS_FIFO_WRDY; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci writel(t_val, host->addr + INT_STATUS_ENABLE); 44262306a36Sopenharmony_ci writel(t_val, host->addr + INT_SIGNAL_ENABLE); 44362306a36Sopenharmony_ci } else { 44462306a36Sopenharmony_ci cmd &= ~(TPC_DATA_SEL | 0xf); 44562306a36Sopenharmony_ci host->cmd_flags |= REG_DATA; 44662306a36Sopenharmony_ci cmd |= data_len & 0xf; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (host->req->data_dir == WRITE) { 44962306a36Sopenharmony_ci jmb38x_ms_transfer_data(host); 45062306a36Sopenharmony_ci writel(host->io_word[0], host->addr + TPC_P0); 45162306a36Sopenharmony_ci writel(host->io_word[1], host->addr + TPC_P1); 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci mod_timer(&host->timer, jiffies + host->timeout_jiffies); 45662306a36Sopenharmony_ci writel(HOST_CONTROL_LED | readl(host->addr + HOST_CONTROL), 45762306a36Sopenharmony_ci host->addr + HOST_CONTROL); 45862306a36Sopenharmony_ci host->req->error = 0; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci writel(cmd, host->addr + TPC); 46162306a36Sopenharmony_ci dev_dbg(&msh->dev, "executing TPC %08x, len %x\n", cmd, data_len); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic void jmb38x_ms_complete_cmd(struct memstick_host *msh, int last) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci struct jmb38x_ms_host *host = memstick_priv(msh); 46962306a36Sopenharmony_ci unsigned int t_val = 0; 47062306a36Sopenharmony_ci int rc; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci del_timer(&host->timer); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci dev_dbg(&msh->dev, "c control %08x\n", 47562306a36Sopenharmony_ci readl(host->addr + HOST_CONTROL)); 47662306a36Sopenharmony_ci dev_dbg(&msh->dev, "c status %08x\n", 47762306a36Sopenharmony_ci readl(host->addr + INT_STATUS)); 47862306a36Sopenharmony_ci dev_dbg(&msh->dev, "c hstatus %08x\n", readl(host->addr + STATUS)); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci host->req->int_reg = readl(host->addr + STATUS) & 0xff; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci writel(0, host->addr + BLOCK); 48362306a36Sopenharmony_ci writel(0, host->addr + DMA_CONTROL); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci if (host->cmd_flags & DMA_DATA) { 48662306a36Sopenharmony_ci dma_unmap_sg(&host->chip->pdev->dev, &host->req->sg, 1, 48762306a36Sopenharmony_ci host->req->data_dir == READ 48862306a36Sopenharmony_ci ? DMA_FROM_DEVICE : DMA_TO_DEVICE); 48962306a36Sopenharmony_ci } else { 49062306a36Sopenharmony_ci t_val = readl(host->addr + INT_STATUS_ENABLE); 49162306a36Sopenharmony_ci if (host->req->data_dir == READ) 49262306a36Sopenharmony_ci t_val &= ~INT_STATUS_FIFO_RRDY; 49362306a36Sopenharmony_ci else 49462306a36Sopenharmony_ci t_val &= ~INT_STATUS_FIFO_WRDY; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci writel(t_val, host->addr + INT_STATUS_ENABLE); 49762306a36Sopenharmony_ci writel(t_val, host->addr + INT_SIGNAL_ENABLE); 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci writel((~HOST_CONTROL_LED) & readl(host->addr + HOST_CONTROL), 50162306a36Sopenharmony_ci host->addr + HOST_CONTROL); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (!last) { 50462306a36Sopenharmony_ci do { 50562306a36Sopenharmony_ci rc = memstick_next_req(msh, &host->req); 50662306a36Sopenharmony_ci } while (!rc && jmb38x_ms_issue_cmd(msh)); 50762306a36Sopenharmony_ci } else { 50862306a36Sopenharmony_ci do { 50962306a36Sopenharmony_ci rc = memstick_next_req(msh, &host->req); 51062306a36Sopenharmony_ci if (!rc) 51162306a36Sopenharmony_ci host->req->error = -ETIME; 51262306a36Sopenharmony_ci } while (!rc); 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic irqreturn_t jmb38x_ms_isr(int irq, void *dev_id) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci struct memstick_host *msh = dev_id; 51962306a36Sopenharmony_ci struct jmb38x_ms_host *host = memstick_priv(msh); 52062306a36Sopenharmony_ci unsigned int irq_status; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci spin_lock(&host->lock); 52362306a36Sopenharmony_ci irq_status = readl(host->addr + INT_STATUS); 52462306a36Sopenharmony_ci dev_dbg(&host->chip->pdev->dev, "irq_status = %08x\n", irq_status); 52562306a36Sopenharmony_ci if (irq_status == 0 || irq_status == (~0)) { 52662306a36Sopenharmony_ci spin_unlock(&host->lock); 52762306a36Sopenharmony_ci return IRQ_NONE; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (host->req) { 53162306a36Sopenharmony_ci if (irq_status & INT_STATUS_ANY_ERR) { 53262306a36Sopenharmony_ci if (irq_status & INT_STATUS_CRC_ERR) 53362306a36Sopenharmony_ci host->req->error = -EILSEQ; 53462306a36Sopenharmony_ci else if (irq_status & INT_STATUS_TPC_ERR) { 53562306a36Sopenharmony_ci dev_dbg(&host->chip->pdev->dev, "TPC_ERR\n"); 53662306a36Sopenharmony_ci jmb38x_ms_complete_cmd(msh, 0); 53762306a36Sopenharmony_ci } else 53862306a36Sopenharmony_ci host->req->error = -ETIME; 53962306a36Sopenharmony_ci } else { 54062306a36Sopenharmony_ci if (host->cmd_flags & DMA_DATA) { 54162306a36Sopenharmony_ci if (irq_status & INT_STATUS_EOTRAN) 54262306a36Sopenharmony_ci host->cmd_flags |= FIFO_READY; 54362306a36Sopenharmony_ci } else { 54462306a36Sopenharmony_ci if (irq_status & (INT_STATUS_FIFO_RRDY 54562306a36Sopenharmony_ci | INT_STATUS_FIFO_WRDY)) 54662306a36Sopenharmony_ci jmb38x_ms_transfer_data(host); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if (irq_status & INT_STATUS_EOTRAN) { 54962306a36Sopenharmony_ci jmb38x_ms_transfer_data(host); 55062306a36Sopenharmony_ci host->cmd_flags |= FIFO_READY; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (irq_status & INT_STATUS_EOTPC) { 55562306a36Sopenharmony_ci host->cmd_flags |= CMD_READY; 55662306a36Sopenharmony_ci if (host->cmd_flags & REG_DATA) { 55762306a36Sopenharmony_ci if (host->req->data_dir == READ) { 55862306a36Sopenharmony_ci host->io_word[0] 55962306a36Sopenharmony_ci = readl(host->addr 56062306a36Sopenharmony_ci + TPC_P0); 56162306a36Sopenharmony_ci host->io_word[1] 56262306a36Sopenharmony_ci = readl(host->addr 56362306a36Sopenharmony_ci + TPC_P1); 56462306a36Sopenharmony_ci host->io_pos = 8; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci jmb38x_ms_transfer_data(host); 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci host->cmd_flags |= FIFO_READY; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci if (irq_status & (INT_STATUS_MEDIA_IN | INT_STATUS_MEDIA_OUT)) { 57562306a36Sopenharmony_ci dev_dbg(&host->chip->pdev->dev, "media changed\n"); 57662306a36Sopenharmony_ci memstick_detect_change(msh); 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci writel(irq_status, host->addr + INT_STATUS); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (host->req 58262306a36Sopenharmony_ci && (((host->cmd_flags & CMD_READY) 58362306a36Sopenharmony_ci && (host->cmd_flags & FIFO_READY)) 58462306a36Sopenharmony_ci || host->req->error)) 58562306a36Sopenharmony_ci jmb38x_ms_complete_cmd(msh, 0); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci spin_unlock(&host->lock); 58862306a36Sopenharmony_ci return IRQ_HANDLED; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic void jmb38x_ms_abort(struct timer_list *t) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci struct jmb38x_ms_host *host = from_timer(host, t, timer); 59462306a36Sopenharmony_ci struct memstick_host *msh = host->msh; 59562306a36Sopenharmony_ci unsigned long flags; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci dev_dbg(&host->chip->pdev->dev, "abort\n"); 59862306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 59962306a36Sopenharmony_ci if (host->req) { 60062306a36Sopenharmony_ci host->req->error = -ETIME; 60162306a36Sopenharmony_ci jmb38x_ms_complete_cmd(msh, 0); 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic void jmb38x_ms_req_tasklet(unsigned long data) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci struct memstick_host *msh = (struct memstick_host *)data; 60962306a36Sopenharmony_ci struct jmb38x_ms_host *host = memstick_priv(msh); 61062306a36Sopenharmony_ci unsigned long flags; 61162306a36Sopenharmony_ci int rc; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 61462306a36Sopenharmony_ci if (!host->req) { 61562306a36Sopenharmony_ci do { 61662306a36Sopenharmony_ci rc = memstick_next_req(msh, &host->req); 61762306a36Sopenharmony_ci dev_dbg(&host->chip->pdev->dev, "tasklet req %d\n", rc); 61862306a36Sopenharmony_ci } while (!rc && jmb38x_ms_issue_cmd(msh)); 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic void jmb38x_ms_dummy_submit(struct memstick_host *msh) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci return; 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic void jmb38x_ms_submit_req(struct memstick_host *msh) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci struct jmb38x_ms_host *host = memstick_priv(msh); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci tasklet_schedule(&host->notify); 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic int jmb38x_ms_reset(struct jmb38x_ms_host *host) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci int cnt; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci writel(HOST_CONTROL_RESET_REQ | HOST_CONTROL_CLOCK_EN 64062306a36Sopenharmony_ci | readl(host->addr + HOST_CONTROL), 64162306a36Sopenharmony_ci host->addr + HOST_CONTROL); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci for (cnt = 0; cnt < 20; ++cnt) { 64462306a36Sopenharmony_ci if (!(HOST_CONTROL_RESET_REQ 64562306a36Sopenharmony_ci & readl(host->addr + HOST_CONTROL))) 64662306a36Sopenharmony_ci goto reset_next; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci ndelay(20); 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci dev_dbg(&host->chip->pdev->dev, "reset_req timeout\n"); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cireset_next: 65362306a36Sopenharmony_ci writel(HOST_CONTROL_RESET | HOST_CONTROL_CLOCK_EN 65462306a36Sopenharmony_ci | readl(host->addr + HOST_CONTROL), 65562306a36Sopenharmony_ci host->addr + HOST_CONTROL); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci for (cnt = 0; cnt < 20; ++cnt) { 65862306a36Sopenharmony_ci if (!(HOST_CONTROL_RESET 65962306a36Sopenharmony_ci & readl(host->addr + HOST_CONTROL))) 66062306a36Sopenharmony_ci goto reset_ok; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci ndelay(20); 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci dev_dbg(&host->chip->pdev->dev, "reset timeout\n"); 66562306a36Sopenharmony_ci return -EIO; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_cireset_ok: 66862306a36Sopenharmony_ci writel(INT_STATUS_ALL, host->addr + INT_SIGNAL_ENABLE); 66962306a36Sopenharmony_ci writel(INT_STATUS_ALL, host->addr + INT_STATUS_ENABLE); 67062306a36Sopenharmony_ci return 0; 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic int jmb38x_ms_set_param(struct memstick_host *msh, 67462306a36Sopenharmony_ci enum memstick_param param, 67562306a36Sopenharmony_ci int value) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci struct jmb38x_ms_host *host = memstick_priv(msh); 67862306a36Sopenharmony_ci unsigned int host_ctl = readl(host->addr + HOST_CONTROL); 67962306a36Sopenharmony_ci unsigned int clock_ctl = CLOCK_CONTROL_BY_MMIO, clock_delay = 0; 68062306a36Sopenharmony_ci int rc = 0; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci switch (param) { 68362306a36Sopenharmony_ci case MEMSTICK_POWER: 68462306a36Sopenharmony_ci if (value == MEMSTICK_POWER_ON) { 68562306a36Sopenharmony_ci rc = jmb38x_ms_reset(host); 68662306a36Sopenharmony_ci if (rc) 68762306a36Sopenharmony_ci return rc; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci host_ctl = 7; 69062306a36Sopenharmony_ci host_ctl |= HOST_CONTROL_POWER_EN 69162306a36Sopenharmony_ci | HOST_CONTROL_CLOCK_EN; 69262306a36Sopenharmony_ci writel(host_ctl, host->addr + HOST_CONTROL); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci writel(host->id ? PAD_PU_PD_ON_MS_SOCK1 69562306a36Sopenharmony_ci : PAD_PU_PD_ON_MS_SOCK0, 69662306a36Sopenharmony_ci host->addr + PAD_PU_PD); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci writel(PAD_OUTPUT_ENABLE_MS, 69962306a36Sopenharmony_ci host->addr + PAD_OUTPUT_ENABLE); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci msleep(10); 70262306a36Sopenharmony_ci dev_dbg(&host->chip->pdev->dev, "power on\n"); 70362306a36Sopenharmony_ci } else if (value == MEMSTICK_POWER_OFF) { 70462306a36Sopenharmony_ci host_ctl &= ~(HOST_CONTROL_POWER_EN 70562306a36Sopenharmony_ci | HOST_CONTROL_CLOCK_EN); 70662306a36Sopenharmony_ci writel(host_ctl, host->addr + HOST_CONTROL); 70762306a36Sopenharmony_ci writel(0, host->addr + PAD_OUTPUT_ENABLE); 70862306a36Sopenharmony_ci writel(PAD_PU_PD_OFF, host->addr + PAD_PU_PD); 70962306a36Sopenharmony_ci dev_dbg(&host->chip->pdev->dev, "power off\n"); 71062306a36Sopenharmony_ci } else 71162306a36Sopenharmony_ci return -EINVAL; 71262306a36Sopenharmony_ci break; 71362306a36Sopenharmony_ci case MEMSTICK_INTERFACE: 71462306a36Sopenharmony_ci dev_dbg(&host->chip->pdev->dev, 71562306a36Sopenharmony_ci "Set Host Interface Mode to %d\n", value); 71662306a36Sopenharmony_ci host_ctl &= ~(HOST_CONTROL_FAST_CLK | HOST_CONTROL_REI | 71762306a36Sopenharmony_ci HOST_CONTROL_REO); 71862306a36Sopenharmony_ci host_ctl |= HOST_CONTROL_TDELAY_EN | HOST_CONTROL_HW_OC_P; 71962306a36Sopenharmony_ci host_ctl &= ~(3 << HOST_CONTROL_IF_SHIFT); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci if (value == MEMSTICK_SERIAL) { 72262306a36Sopenharmony_ci host_ctl |= HOST_CONTROL_IF_SERIAL 72362306a36Sopenharmony_ci << HOST_CONTROL_IF_SHIFT; 72462306a36Sopenharmony_ci host_ctl |= HOST_CONTROL_REI; 72562306a36Sopenharmony_ci clock_ctl |= CLOCK_CONTROL_40MHZ; 72662306a36Sopenharmony_ci clock_delay = 0; 72762306a36Sopenharmony_ci } else if (value == MEMSTICK_PAR4) { 72862306a36Sopenharmony_ci host_ctl |= HOST_CONTROL_FAST_CLK; 72962306a36Sopenharmony_ci host_ctl |= HOST_CONTROL_IF_PAR4 73062306a36Sopenharmony_ci << HOST_CONTROL_IF_SHIFT; 73162306a36Sopenharmony_ci host_ctl |= HOST_CONTROL_REO; 73262306a36Sopenharmony_ci clock_ctl |= CLOCK_CONTROL_40MHZ; 73362306a36Sopenharmony_ci clock_delay = 4; 73462306a36Sopenharmony_ci } else if (value == MEMSTICK_PAR8) { 73562306a36Sopenharmony_ci host_ctl |= HOST_CONTROL_FAST_CLK; 73662306a36Sopenharmony_ci host_ctl |= HOST_CONTROL_IF_PAR8 73762306a36Sopenharmony_ci << HOST_CONTROL_IF_SHIFT; 73862306a36Sopenharmony_ci clock_ctl |= CLOCK_CONTROL_50MHZ; 73962306a36Sopenharmony_ci clock_delay = 0; 74062306a36Sopenharmony_ci } else 74162306a36Sopenharmony_ci return -EINVAL; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci writel(host_ctl, host->addr + HOST_CONTROL); 74462306a36Sopenharmony_ci writel(CLOCK_CONTROL_OFF, host->addr + CLOCK_CONTROL); 74562306a36Sopenharmony_ci writel(clock_ctl, host->addr + CLOCK_CONTROL); 74662306a36Sopenharmony_ci pci_write_config_byte(host->chip->pdev, 74762306a36Sopenharmony_ci PCI_CTL_CLOCK_DLY_ADDR + 1, 74862306a36Sopenharmony_ci clock_delay); 74962306a36Sopenharmony_ci host->ifmode = value; 75062306a36Sopenharmony_ci break; 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci return 0; 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci#define PCI_PMOS0_CONTROL 0xae 75662306a36Sopenharmony_ci#define PMOS0_ENABLE 0x01 75762306a36Sopenharmony_ci#define PMOS0_OVERCURRENT_LEVEL_2_4V 0x06 75862306a36Sopenharmony_ci#define PMOS0_EN_OVERCURRENT_DEBOUNCE 0x40 75962306a36Sopenharmony_ci#define PMOS0_SW_LED_POLARITY_ENABLE 0x80 76062306a36Sopenharmony_ci#define PMOS0_ACTIVE_BITS (PMOS0_ENABLE | PMOS0_EN_OVERCURRENT_DEBOUNCE | \ 76162306a36Sopenharmony_ci PMOS0_OVERCURRENT_LEVEL_2_4V) 76262306a36Sopenharmony_ci#define PCI_PMOS1_CONTROL 0xbd 76362306a36Sopenharmony_ci#define PMOS1_ACTIVE_BITS 0x4a 76462306a36Sopenharmony_ci#define PCI_CLOCK_CTL 0xb9 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_cistatic int jmb38x_ms_pmos(struct pci_dev *pdev, int flag) 76762306a36Sopenharmony_ci{ 76862306a36Sopenharmony_ci unsigned char val; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci pci_read_config_byte(pdev, PCI_PMOS0_CONTROL, &val); 77162306a36Sopenharmony_ci if (flag) 77262306a36Sopenharmony_ci val |= PMOS0_ACTIVE_BITS; 77362306a36Sopenharmony_ci else 77462306a36Sopenharmony_ci val &= ~PMOS0_ACTIVE_BITS; 77562306a36Sopenharmony_ci pci_write_config_byte(pdev, PCI_PMOS0_CONTROL, val); 77662306a36Sopenharmony_ci dev_dbg(&pdev->dev, "JMB38x: set PMOS0 val 0x%x\n", val); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci if (pci_resource_flags(pdev, 1)) { 77962306a36Sopenharmony_ci pci_read_config_byte(pdev, PCI_PMOS1_CONTROL, &val); 78062306a36Sopenharmony_ci if (flag) 78162306a36Sopenharmony_ci val |= PMOS1_ACTIVE_BITS; 78262306a36Sopenharmony_ci else 78362306a36Sopenharmony_ci val &= ~PMOS1_ACTIVE_BITS; 78462306a36Sopenharmony_ci pci_write_config_byte(pdev, PCI_PMOS1_CONTROL, val); 78562306a36Sopenharmony_ci dev_dbg(&pdev->dev, "JMB38x: set PMOS1 val 0x%x\n", val); 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci pci_read_config_byte(pdev, PCI_CLOCK_CTL, &val); 78962306a36Sopenharmony_ci pci_write_config_byte(pdev, PCI_CLOCK_CTL, val & ~0x0f); 79062306a36Sopenharmony_ci pci_write_config_byte(pdev, PCI_CLOCK_CTL, val | 0x01); 79162306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Clock Control by PCI config is disabled!\n"); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci return 0; 79462306a36Sopenharmony_ci} 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_cistatic int __maybe_unused jmb38x_ms_suspend(struct device *dev) 79762306a36Sopenharmony_ci{ 79862306a36Sopenharmony_ci struct jmb38x_ms *jm = dev_get_drvdata(dev); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci int cnt; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci for (cnt = 0; cnt < jm->host_cnt; ++cnt) { 80362306a36Sopenharmony_ci if (!jm->hosts[cnt]) 80462306a36Sopenharmony_ci break; 80562306a36Sopenharmony_ci memstick_suspend_host(jm->hosts[cnt]); 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci device_wakeup_disable(dev); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci return 0; 81162306a36Sopenharmony_ci} 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_cistatic int __maybe_unused jmb38x_ms_resume(struct device *dev) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci struct jmb38x_ms *jm = dev_get_drvdata(dev); 81662306a36Sopenharmony_ci int rc; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci jmb38x_ms_pmos(to_pci_dev(dev), 1); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci for (rc = 0; rc < jm->host_cnt; ++rc) { 82162306a36Sopenharmony_ci if (!jm->hosts[rc]) 82262306a36Sopenharmony_ci break; 82362306a36Sopenharmony_ci memstick_resume_host(jm->hosts[rc]); 82462306a36Sopenharmony_ci memstick_detect_change(jm->hosts[rc]); 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci return 0; 82862306a36Sopenharmony_ci} 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_cistatic int jmb38x_ms_count_slots(struct pci_dev *pdev) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci int cnt, rc = 0; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci for (cnt = 0; cnt < PCI_STD_NUM_BARS; ++cnt) { 83562306a36Sopenharmony_ci if (!(IORESOURCE_MEM & pci_resource_flags(pdev, cnt))) 83662306a36Sopenharmony_ci break; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci if (256 != pci_resource_len(pdev, cnt)) 83962306a36Sopenharmony_ci break; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci ++rc; 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci return rc; 84462306a36Sopenharmony_ci} 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_cistatic struct memstick_host *jmb38x_ms_alloc_host(struct jmb38x_ms *jm, int cnt) 84762306a36Sopenharmony_ci{ 84862306a36Sopenharmony_ci struct memstick_host *msh; 84962306a36Sopenharmony_ci struct jmb38x_ms_host *host; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci msh = memstick_alloc_host(sizeof(struct jmb38x_ms_host), 85262306a36Sopenharmony_ci &jm->pdev->dev); 85362306a36Sopenharmony_ci if (!msh) 85462306a36Sopenharmony_ci return NULL; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci host = memstick_priv(msh); 85762306a36Sopenharmony_ci host->msh = msh; 85862306a36Sopenharmony_ci host->chip = jm; 85962306a36Sopenharmony_ci host->addr = ioremap(pci_resource_start(jm->pdev, cnt), 86062306a36Sopenharmony_ci pci_resource_len(jm->pdev, cnt)); 86162306a36Sopenharmony_ci if (!host->addr) 86262306a36Sopenharmony_ci goto err_out_free; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci spin_lock_init(&host->lock); 86562306a36Sopenharmony_ci host->id = cnt; 86662306a36Sopenharmony_ci snprintf(host->host_id, sizeof(host->host_id), DRIVER_NAME ":slot%d", 86762306a36Sopenharmony_ci host->id); 86862306a36Sopenharmony_ci host->irq = jm->pdev->irq; 86962306a36Sopenharmony_ci host->timeout_jiffies = msecs_to_jiffies(1000); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci tasklet_init(&host->notify, jmb38x_ms_req_tasklet, (unsigned long)msh); 87262306a36Sopenharmony_ci msh->request = jmb38x_ms_submit_req; 87362306a36Sopenharmony_ci msh->set_param = jmb38x_ms_set_param; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci msh->caps = MEMSTICK_CAP_PAR4 | MEMSTICK_CAP_PAR8; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci timer_setup(&host->timer, jmb38x_ms_abort, 0); 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci if (!request_irq(host->irq, jmb38x_ms_isr, IRQF_SHARED, host->host_id, 88062306a36Sopenharmony_ci msh)) 88162306a36Sopenharmony_ci return msh; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci iounmap(host->addr); 88462306a36Sopenharmony_cierr_out_free: 88562306a36Sopenharmony_ci memstick_free_host(msh); 88662306a36Sopenharmony_ci return NULL; 88762306a36Sopenharmony_ci} 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_cistatic void jmb38x_ms_free_host(struct memstick_host *msh) 89062306a36Sopenharmony_ci{ 89162306a36Sopenharmony_ci struct jmb38x_ms_host *host = memstick_priv(msh); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci free_irq(host->irq, msh); 89462306a36Sopenharmony_ci iounmap(host->addr); 89562306a36Sopenharmony_ci memstick_free_host(msh); 89662306a36Sopenharmony_ci} 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_cistatic int jmb38x_ms_probe(struct pci_dev *pdev, 89962306a36Sopenharmony_ci const struct pci_device_id *dev_id) 90062306a36Sopenharmony_ci{ 90162306a36Sopenharmony_ci struct jmb38x_ms *jm; 90262306a36Sopenharmony_ci int pci_dev_busy = 0; 90362306a36Sopenharmony_ci int rc, cnt; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); 90662306a36Sopenharmony_ci if (rc) 90762306a36Sopenharmony_ci return rc; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci rc = pci_enable_device(pdev); 91062306a36Sopenharmony_ci if (rc) 91162306a36Sopenharmony_ci return rc; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci pci_set_master(pdev); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci rc = pci_request_regions(pdev, DRIVER_NAME); 91662306a36Sopenharmony_ci if (rc) { 91762306a36Sopenharmony_ci pci_dev_busy = 1; 91862306a36Sopenharmony_ci goto err_out; 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci jmb38x_ms_pmos(pdev, 1); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci cnt = jmb38x_ms_count_slots(pdev); 92462306a36Sopenharmony_ci if (!cnt) { 92562306a36Sopenharmony_ci rc = -ENODEV; 92662306a36Sopenharmony_ci pci_dev_busy = 1; 92762306a36Sopenharmony_ci goto err_out_int; 92862306a36Sopenharmony_ci } 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci jm = kzalloc(struct_size(jm, hosts, cnt), GFP_KERNEL); 93162306a36Sopenharmony_ci if (!jm) { 93262306a36Sopenharmony_ci rc = -ENOMEM; 93362306a36Sopenharmony_ci goto err_out_int; 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci jm->pdev = pdev; 93762306a36Sopenharmony_ci jm->host_cnt = cnt; 93862306a36Sopenharmony_ci pci_set_drvdata(pdev, jm); 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci for (cnt = 0; cnt < jm->host_cnt; ++cnt) { 94162306a36Sopenharmony_ci jm->hosts[cnt] = jmb38x_ms_alloc_host(jm, cnt); 94262306a36Sopenharmony_ci if (!jm->hosts[cnt]) 94362306a36Sopenharmony_ci break; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci rc = memstick_add_host(jm->hosts[cnt]); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci if (rc) { 94862306a36Sopenharmony_ci jmb38x_ms_free_host(jm->hosts[cnt]); 94962306a36Sopenharmony_ci jm->hosts[cnt] = NULL; 95062306a36Sopenharmony_ci break; 95162306a36Sopenharmony_ci } 95262306a36Sopenharmony_ci } 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci if (cnt) 95562306a36Sopenharmony_ci return 0; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci rc = -ENODEV; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 96062306a36Sopenharmony_ci kfree(jm); 96162306a36Sopenharmony_cierr_out_int: 96262306a36Sopenharmony_ci pci_release_regions(pdev); 96362306a36Sopenharmony_cierr_out: 96462306a36Sopenharmony_ci if (!pci_dev_busy) 96562306a36Sopenharmony_ci pci_disable_device(pdev); 96662306a36Sopenharmony_ci return rc; 96762306a36Sopenharmony_ci} 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_cistatic void jmb38x_ms_remove(struct pci_dev *dev) 97062306a36Sopenharmony_ci{ 97162306a36Sopenharmony_ci struct jmb38x_ms *jm = pci_get_drvdata(dev); 97262306a36Sopenharmony_ci struct jmb38x_ms_host *host; 97362306a36Sopenharmony_ci int cnt; 97462306a36Sopenharmony_ci unsigned long flags; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci for (cnt = 0; cnt < jm->host_cnt; ++cnt) { 97762306a36Sopenharmony_ci if (!jm->hosts[cnt]) 97862306a36Sopenharmony_ci break; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci host = memstick_priv(jm->hosts[cnt]); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci jm->hosts[cnt]->request = jmb38x_ms_dummy_submit; 98362306a36Sopenharmony_ci tasklet_kill(&host->notify); 98462306a36Sopenharmony_ci writel(0, host->addr + INT_SIGNAL_ENABLE); 98562306a36Sopenharmony_ci writel(0, host->addr + INT_STATUS_ENABLE); 98662306a36Sopenharmony_ci dev_dbg(&jm->pdev->dev, "interrupts off\n"); 98762306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 98862306a36Sopenharmony_ci if (host->req) { 98962306a36Sopenharmony_ci host->req->error = -ETIME; 99062306a36Sopenharmony_ci jmb38x_ms_complete_cmd(jm->hosts[cnt], 1); 99162306a36Sopenharmony_ci } 99262306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci memstick_remove_host(jm->hosts[cnt]); 99562306a36Sopenharmony_ci dev_dbg(&jm->pdev->dev, "host removed\n"); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci jmb38x_ms_free_host(jm->hosts[cnt]); 99862306a36Sopenharmony_ci } 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci jmb38x_ms_pmos(dev, 0); 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci pci_set_drvdata(dev, NULL); 100362306a36Sopenharmony_ci pci_release_regions(dev); 100462306a36Sopenharmony_ci pci_disable_device(dev); 100562306a36Sopenharmony_ci kfree(jm); 100662306a36Sopenharmony_ci} 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_cistatic struct pci_device_id jmb38x_ms_id_tbl [] = { 100962306a36Sopenharmony_ci { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB38X_MS) }, 101062306a36Sopenharmony_ci { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB385_MS) }, 101162306a36Sopenharmony_ci { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB390_MS) }, 101262306a36Sopenharmony_ci { } 101362306a36Sopenharmony_ci}; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(jmb38x_ms_pm_ops, jmb38x_ms_suspend, jmb38x_ms_resume); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_cistatic struct pci_driver jmb38x_ms_driver = { 101862306a36Sopenharmony_ci .name = DRIVER_NAME, 101962306a36Sopenharmony_ci .id_table = jmb38x_ms_id_tbl, 102062306a36Sopenharmony_ci .probe = jmb38x_ms_probe, 102162306a36Sopenharmony_ci .remove = jmb38x_ms_remove, 102262306a36Sopenharmony_ci .driver.pm = &jmb38x_ms_pm_ops, 102362306a36Sopenharmony_ci}; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_cimodule_pci_driver(jmb38x_ms_driver); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ciMODULE_AUTHOR("Alex Dubov"); 102862306a36Sopenharmony_ciMODULE_DESCRIPTION("JMicron jmb38x MemoryStick driver"); 102962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 103062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, jmb38x_ms_id_tbl); 1031