Commit 4de24fcf authored by Russell King's avatar Russell King

[PCMCIA] Don't cache CIS bytes found to be invalid.

Several PCMCIA cards I have here do not work correctly over a
suspend/resume cycle; the PCMCIA code believes that the card has
been changed in the slot, and therefore performs a remove/insert
cycle.

This seems to be because the card returns more or less random data
when reading memory space, leading to the CIS cache mismatching
the card data.  This in turn is caused because we try to read CIS
data from both the attribute and memory spaces, and we add the result
to the CIS cache whether or not the returned data was valid.

We therefore convert the CIS cache to use a linked list, and provide
a way to remove cached data from that list.  We also replace the
"s->cis_used=0;" construct with a function "destroy_cis_cache(s)"
which clearly describes what we're doing.
parent 568c3355
...@@ -269,8 +269,8 @@ void write_cis_mem(socket_info_t *s, int attr, u_int addr, ...@@ -269,8 +269,8 @@ void write_cis_mem(socket_info_t *s, int attr, u_int addr,
static void read_cis_cache(socket_info_t *s, int attr, u_int addr, static void read_cis_cache(socket_info_t *s, int attr, u_int addr,
u_int len, void *ptr) u_int len, void *ptr)
{ {
int i, ret; struct cis_cache_entry *cis;
char *caddr; int ret;
if (s->fake_cis) { if (s->fake_cis) {
if (s->fake_cis_len > addr+len) if (s->fake_cis_len > addr+len)
...@@ -279,31 +279,54 @@ static void read_cis_cache(socket_info_t *s, int attr, u_int addr, ...@@ -279,31 +279,54 @@ static void read_cis_cache(socket_info_t *s, int attr, u_int addr,
memset(ptr, 0xff, len); memset(ptr, 0xff, len);
return; return;
} }
caddr = s->cis_cache;
for (i = 0; i < s->cis_used; i++) { list_for_each_entry(cis, &s->cis_cache, node) {
if ((s->cis_table[i].addr == addr) && if (cis->addr == addr && cis->len == len && cis->attr == attr) {
(s->cis_table[i].len == len) && memcpy(ptr, cis->cache, len);
(s->cis_table[i].attr == attr)) break;
caddr += s->cis_table[i].len;
}
if (i < s->cis_used) {
memcpy(ptr, caddr, len);
return; return;
} }
}
#ifdef CONFIG_CARDBUS #ifdef CONFIG_CARDBUS
if (s->state & SOCKET_CARDBUS) if (s->state & SOCKET_CARDBUS)
ret = read_cb_mem(s, attr, addr, len, ptr); ret = read_cb_mem(s, attr, addr, len, ptr);
else else
#endif #endif
ret = read_cis_mem(s, attr, addr, len, ptr); ret = read_cis_mem(s, attr, addr, len, ptr);
/* Copy data into the cache, if there is room */
if ((ret == 0) && (i < MAX_CIS_TABLE) && /* Copy data into the cache */
(caddr+len < s->cis_cache+MAX_CIS_DATA)) { cis = kmalloc(sizeof(struct cis_cache_entry) + len, GFP_KERNEL);
s->cis_table[i].addr = addr; if (cis) {
s->cis_table[i].len = len; cis->addr = addr;
s->cis_table[i].attr = attr; cis->len = len;
s->cis_used++; cis->attr = attr;
memcpy(caddr, ptr, len); memcpy(cis->cache, ptr, len);
list_add(&cis->node, &s->cis_cache);
}
}
static void
remove_cis_cache(socket_info_t *s, int attr, u_int addr, u_int len)
{
struct cis_cache_entry *cis;
list_for_each_entry(cis, &s->cis_cache, node)
if (cis->addr == addr && cis->len == len && cis->attr == attr) {
list_del(&cis->node);
kfree(cis);
break;
}
}
void destroy_cis_cache(socket_info_t *s)
{
struct list_head *l, *n;
list_for_each_safe(l, n, &s->cis_cache) {
struct cis_cache_entry *cis = list_entry(l, struct cis_cache_entry, node);
list_del(&cis->node);
kfree(cis);
} }
} }
...@@ -316,24 +339,25 @@ static void read_cis_cache(socket_info_t *s, int attr, u_int addr, ...@@ -316,24 +339,25 @@ static void read_cis_cache(socket_info_t *s, int attr, u_int addr,
int verify_cis_cache(socket_info_t *s) int verify_cis_cache(socket_info_t *s)
{ {
char buf[256], *caddr; struct cis_cache_entry *cis;
int i; char buf[256];
list_for_each_entry(cis, &s->cis_cache, node) {
int len = cis->len;
caddr = s->cis_cache; if (len > 256)
for (i = 0; i < s->cis_used; i++) { len = 256;
#ifdef CONFIG_CARDBUS #ifdef CONFIG_CARDBUS
if (s->state & SOCKET_CARDBUS) if (s->state & SOCKET_CARDBUS)
read_cb_mem(s, s->cis_table[i].attr, s->cis_table[i].addr, read_cb_mem(s, cis->attr, cis->addr, len, buf);
s->cis_table[i].len, buf);
else else
#endif #endif
read_cis_mem(s, s->cis_table[i].attr, s->cis_table[i].addr, read_cis_mem(s, cis->attr, cis->addr, len, buf);
s->cis_table[i].len, buf);
if (memcmp(buf, caddr, s->cis_table[i].len) != 0) if (memcmp(buf, cis->cache, len) != 0)
break; return -1;
caddr += s->cis_table[i].len;
} }
return (i < s->cis_used); return 0;
} }
/*====================================================================== /*======================================================================
...@@ -449,14 +473,16 @@ static int follow_link(socket_info_t *s, tuple_t *tuple) ...@@ -449,14 +473,16 @@ static int follow_link(socket_info_t *s, tuple_t *tuple)
if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) && if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
(strncmp(link+2, "CIS", 3) == 0)) (strncmp(link+2, "CIS", 3) == 0))
return ofs; return ofs;
remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5);
/* Then, we try the wrong spot... */ /* Then, we try the wrong spot... */
ofs = ofs >> 1; ofs = ofs >> 1;
} }
read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link); read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
if ((link[0] != CISTPL_LINKTARGET) || (link[1] < 3) || if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
(strncmp(link+2, "CIS", 3) != 0)) (strncmp(link+2, "CIS", 3) == 0))
return -1;
return ofs; return ofs;
remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5);
return -1;
} }
int pcmcia_get_next_tuple(client_handle_t handle, tuple_t *tuple) int pcmcia_get_next_tuple(client_handle_t handle, tuple_t *tuple)
......
...@@ -342,6 +342,7 @@ int pcmcia_register_socket(struct device *dev) ...@@ -342,6 +342,7 @@ int pcmcia_register_socket(struct device *dev)
s->cis_mem.flags = 0; s->cis_mem.flags = 0;
s->cis_mem.speed = cis_speed; s->cis_mem.speed = cis_speed;
s->erase_busy.next = s->erase_busy.prev = &s->erase_busy; s->erase_busy.next = s->erase_busy.prev = &s->erase_busy;
INIT_LIST_HEAD(&s->cis_cache);
spin_lock_init(&s->lock); spin_lock_init(&s->lock);
/* TBD: remove usage of socket_table, use class_for_each_dev instead */ /* TBD: remove usage of socket_table, use class_for_each_dev instead */
...@@ -469,7 +470,7 @@ static void shutdown_socket(socket_info_t *s) ...@@ -469,7 +470,7 @@ static void shutdown_socket(socket_info_t *s)
init_socket(s); init_socket(s);
s->irq.AssignedIRQ = s->irq.Config = 0; s->irq.AssignedIRQ = s->irq.Config = 0;
s->lock_count = 0; s->lock_count = 0;
s->cis_used = 0; destroy_cis_cache(s);
if (s->fake_cis) { if (s->fake_cis) {
kfree(s->fake_cis); kfree(s->fake_cis);
s->fake_cis = NULL; s->fake_cis = NULL;
......
...@@ -114,9 +114,13 @@ typedef struct config_t { ...@@ -114,9 +114,13 @@ typedef struct config_t {
/* Maximum number of memory windows per socket */ /* Maximum number of memory windows per socket */
#define MAX_WIN 4 #define MAX_WIN 4
/* The size of the CIS cache */ struct cis_cache_entry {
#define MAX_CIS_TABLE 64 struct list_head node;
#define MAX_CIS_DATA 512 unsigned int addr;
unsigned int len;
unsigned int attr;
unsigned char cache[0];
};
typedef struct socket_info_t { typedef struct socket_info_t {
spinlock_t lock; spinlock_t lock;
...@@ -145,13 +149,7 @@ typedef struct socket_info_t { ...@@ -145,13 +149,7 @@ typedef struct socket_info_t {
window_t win[MAX_WIN]; window_t win[MAX_WIN];
region_t *c_region, *a_region; region_t *c_region, *a_region;
erase_busy_t erase_busy; erase_busy_t erase_busy;
int cis_used; struct list_head cis_cache;
struct {
u_int addr;
u_short len;
u_short attr;
} cis_table[MAX_CIS_TABLE];
char cis_cache[MAX_CIS_DATA];
u_int fake_cis_len; u_int fake_cis_len;
char *fake_cis; char *fake_cis;
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
...@@ -206,6 +204,7 @@ int read_cis_mem(socket_info_t *s, int attr, ...@@ -206,6 +204,7 @@ int read_cis_mem(socket_info_t *s, int attr,
void write_cis_mem(socket_info_t *s, int attr, void write_cis_mem(socket_info_t *s, int attr,
u_int addr, u_int len, void *ptr); u_int addr, u_int len, void *ptr);
void release_cis_mem(socket_info_t *s); void release_cis_mem(socket_info_t *s);
void destroy_cis_cache(socket_info_t *s);
int verify_cis_cache(socket_info_t *s); int verify_cis_cache(socket_info_t *s);
void preload_cis_cache(socket_info_t *s); void preload_cis_cache(socket_info_t *s);
int get_first_tuple(client_handle_t handle, tuple_t *tuple); int get_first_tuple(client_handle_t handle, tuple_t *tuple);
......
...@@ -351,7 +351,7 @@ static int cis_readable(socket_info_t *s, u_long base) ...@@ -351,7 +351,7 @@ static int cis_readable(socket_info_t *s, u_long base)
ret = pcmcia_validate_cis(s->clients, &info1); ret = pcmcia_validate_cis(s->clients, &info1);
/* invalidate mapping and CIS cache */ /* invalidate mapping and CIS cache */
iounmap(s->cis_virt); iounmap(s->cis_virt);
s->cis_used = 0; destroy_cis_cache(s);
if ((ret != 0) || (info1.Chains == 0)) if ((ret != 0) || (info1.Chains == 0))
return 0; return 0;
s->cis_mem.sys_start = base+s->cap.map_size; s->cis_mem.sys_start = base+s->cap.map_size;
...@@ -359,7 +359,7 @@ static int cis_readable(socket_info_t *s, u_long base) ...@@ -359,7 +359,7 @@ static int cis_readable(socket_info_t *s, u_long base)
s->cis_virt = ioremap(base+s->cap.map_size, s->cap.map_size); s->cis_virt = ioremap(base+s->cap.map_size, s->cap.map_size);
ret = pcmcia_validate_cis(s->clients, &info2); ret = pcmcia_validate_cis(s->clients, &info2);
iounmap(s->cis_virt); iounmap(s->cis_virt);
s->cis_used = 0; destroy_cis_cache(s);
return ((ret == 0) && (info1.Chains == info2.Chains)); return ((ret == 0) && (info1.Chains == info2.Chains));
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment