Commit cfc615f2 authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://linux-pnp.bkbits.net/pnp-2.6

into ppc970.osdl.org:/home/torvalds/v2.6/linux
parents a0b18907 7ee49b42
...@@ -563,7 +563,7 @@ $(sort $(vmlinux-objs)) arch/$(ARCH)/kernel/vmlinux.lds.s: $(SUBDIRS) ; ...@@ -563,7 +563,7 @@ $(sort $(vmlinux-objs)) arch/$(ARCH)/kernel/vmlinux.lds.s: $(SUBDIRS) ;
# Handle descending into subdirectories listed in $(SUBDIRS) # Handle descending into subdirectories listed in $(SUBDIRS)
.PHONY: $(SUBDIRS) .PHONY: $(SUBDIRS)
$(SUBDIRS): prepare-all $(SUBDIRS): prepare-all scripts
$(Q)$(MAKE) $(build)=$@ $(Q)$(MAKE) $(build)=$@
# Things we need to do before we recursively start building the kernel # Things we need to do before we recursively start building the kernel
......
...@@ -608,6 +608,7 @@ create_iface(struct device_node *np, struct device *dev) ...@@ -608,6 +608,7 @@ create_iface(struct device_node *np, struct device *dev)
} }
#endif /* POLLED_MODE */ #endif /* POLLED_MODE */
pmac_low_i2c_unlock(np);
dev_set_drvdata(dev, iface); dev_set_drvdata(dev, iface);
for (i=0; i<nchan; i++) { for (i=0; i<nchan; i++) {
...@@ -645,7 +646,6 @@ create_iface(struct device_node *np, struct device *dev) ...@@ -645,7 +646,6 @@ create_iface(struct device_node *np, struct device *dev)
printk(KERN_INFO "Found KeyWest i2c on \"%s\", %d channel%s, stepping: %d bits\n", printk(KERN_INFO "Found KeyWest i2c on \"%s\", %d channel%s, stepping: %d bits\n",
np->parent->name, nchan, nchan > 1 ? "s" : "", bsteps); np->parent->name, nchan, nchan > 1 ? "s" : "", bsteps);
pmac_low_i2c_unlock(np);
return 0; return 0;
} }
......
/* $Id: capifunc.c,v 1.48 2004/01/11 19:20:54 armin Exp $ /* $Id: capifunc.c,v 1.57 2004/03/20 18:18:03 armin Exp $
* *
* ISDN interface module for Eicon active cards DIVA. * ISDN interface module for Eicon active cards DIVA.
* CAPI Interface common functions * CAPI Interface common functions
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "divacapi.h" #include "divacapi.h"
#include "divasync.h" #include "divasync.h"
#include "capifunc.h" #include "capifunc.h"
#include "dlist.h"
#define DBG_MINIMUM (DL_LOG + DL_FTL + DL_ERR) #define DBG_MINIMUM (DL_LOG + DL_FTL + DL_ERR)
#define DBG_DEFAULT (DBG_MINIMUM + DL_XLOG + DL_REG) #define DBG_DEFAULT (DBG_MINIMUM + DL_XLOG + DL_REG)
...@@ -39,9 +40,9 @@ extern word CapiRegister(word); ...@@ -39,9 +40,9 @@ extern word CapiRegister(word);
extern word api_put(APPL *, CAPI_MSG *); extern word api_put(APPL *, CAPI_MSG *);
static diva_os_spin_lock_t api_lock; static diva_os_spin_lock_t api_lock;
static diva_os_spin_lock_t ll_lock;
static diva_card *cards; static LIST_HEAD(cards);
static dword notify_handle; static dword notify_handle;
static void DIRequest(ENTITY * e); static void DIRequest(ENTITY * e);
static DESCRIPTOR MAdapter; static DESCRIPTOR MAdapter;
...@@ -57,6 +58,11 @@ static u16 diva_send_message(struct capi_ctr *, ...@@ -57,6 +58,11 @@ static u16 diva_send_message(struct capi_ctr *,
diva_os_message_buffer_s *); diva_os_message_buffer_s *);
extern void diva_os_set_controller_struct(struct capi_ctr *); extern void diva_os_set_controller_struct(struct capi_ctr *);
/*
* include queue functions
*/
#include "dlist.c"
extern void DIVA_DIDD_Read(DESCRIPTOR *, int); extern void DIVA_DIDD_Read(DESCRIPTOR *, int);
/* /*
...@@ -152,26 +158,15 @@ byte UnMapController(byte MappedController) ...@@ -152,26 +158,15 @@ byte UnMapController(byte MappedController)
static int find_free_id(void) static int find_free_id(void)
{ {
int num = 0; int num = 0;
diva_card *p; DIVA_CAPI_ADAPTER *a;
diva_os_spin_lock_magic_t old_irql;
diva_os_enter_spin_lock(&ll_lock, &old_irql, "find free id"); while (num < MAX_DESCRIPTORS) {
while (num < 100) { a = &adapter[num];
num++; if (!a->Id)
p = cards;
while (p) {
if (p->Id == num)
break; break;
p = p->next; num++;
}
if(!p) {
diva_os_leave_spin_lock(&ll_lock, &old_irql,
"find free id");
return (num);
}
} }
diva_os_leave_spin_lock(&ll_lock, &old_irql, "find free id"); return(num + 1);
return (999);
} }
/* /*
...@@ -179,21 +174,17 @@ static int find_free_id(void) ...@@ -179,21 +174,17 @@ static int find_free_id(void)
*/ */
static diva_card *find_card_by_ctrl(word controller) static diva_card *find_card_by_ctrl(word controller)
{ {
diva_card *p; struct list_head *tmp;
diva_os_spin_lock_magic_t old_irql; diva_card *card;
diva_os_enter_spin_lock(&ll_lock, &old_irql, "find card ctrl");
p = cards;
while (p) { list_for_each(tmp, &cards) {
if (ControllerMap[p->Id] == controller) { card = list_entry(tmp, diva_card, list);
diva_os_leave_spin_lock(&ll_lock, &old_irql, if (ControllerMap[card->Id] == controller) {
"find card ctrl"); if (card->remove_in_progress)
return p; card = NULL;
return(card);
} }
p = p->next;
} }
diva_os_leave_spin_lock(&ll_lock, &old_irql, "find card ctrl");
return (diva_card *) 0; return (diva_card *) 0;
} }
...@@ -358,28 +349,18 @@ void sendf(APPL * appl, word command, dword Id, word Number, byte * format, ...) ...@@ -358,28 +349,18 @@ void sendf(APPL * appl, word command, dword Id, word Number, byte * format, ...)
/* /*
* cleanup adapter * cleanup adapter
*/ */
static void clean_adapter(int id) static void clean_adapter(int id, diva_entity_queue_t* free_mem_q)
{ {
DIVA_CAPI_ADAPTER *a; DIVA_CAPI_ADAPTER *a;
#if IMPLEMENT_LINE_INTERCONNECT2
int i, k; int i, k;
#endif /* IMPLEMENT_LINE_INTERCONNECT2 */
a = &adapter[id]; a = &adapter[id];
#if IMPLEMENT_LINE_INTERCONNECT
if (a->li_pri) {
if (a->li_config.pri)
diva_os_free(0, a->li_config.pri);
} else {
if (a->li_config.bri)
diva_os_free(0, a->li_config.bri);
}
#endif /* IMPLEMENT_LINE_INTERCONNECT */
#if IMPLEMENT_LINE_INTERCONNECT2
k = li_total_channels - a->li_channels; k = li_total_channels - a->li_channels;
if (k == 0) { if (k == 0) {
diva_os_free(0, li_config_table); if (li_config_table) {
diva_q_add_tail(free_mem_q, (diva_entity_link_t*)li_config_table);
li_config_table = NULL; li_config_table = NULL;
}
} else { } else {
if (a->li_base < k) { if (a->li_base < k) {
memmove(&li_config_table[a->li_base], memmove(&li_config_table[a->li_base],
...@@ -401,9 +382,8 @@ static void clean_adapter(int id) ...@@ -401,9 +382,8 @@ static void clean_adapter(int id)
if (adapter[i].request) if (adapter[i].request)
adapter[i].li_base -= a->li_channels; adapter[i].li_base -= a->li_channels;
} }
#endif /* IMPLEMENT_LINE_INTERCONNECT2 */
if (a->plci) if (a->plci)
diva_os_free(0, a->plci); diva_q_add_tail(free_mem_q, (diva_entity_link_t*)a->plci);
memset(a, 0x00, sizeof(DIVA_CAPI_ADAPTER)); memset(a, 0x00, sizeof(DIVA_CAPI_ADAPTER));
while ((max_adapter != 0) && !adapter[max_adapter - 1].request) while ((max_adapter != 0) && !adapter[max_adapter - 1].request)
...@@ -411,67 +391,87 @@ static void clean_adapter(int id) ...@@ -411,67 +391,87 @@ static void clean_adapter(int id)
} }
/* /*
* remove cards * remove a card, but ensures consistent state of LI tables
* in the time adapter is removed
*/ */
static void DIVA_EXIT_FUNCTION divacapi_remove_cards(void) static void divacapi_remove_card(DESCRIPTOR * d)
{ {
diva_card *last; struct list_head *tmp;
diva_card *card; diva_card *card = NULL;
diva_entity_queue_t free_mem_q;
diva_entity_link_t* link;
diva_os_spin_lock_magic_t old_irql; diva_os_spin_lock_magic_t old_irql;
diva_os_enter_spin_lock(&ll_lock, &old_irql, "remove cards"); diva_q_init (&free_mem_q);
card = cards;
while (card) { /*
* Set "remove in progress flag".
* Ensures that there is no call from sendf to CAPI in
* the time CAPI controller is about to be removed.
*/
diva_os_enter_spin_lock(&api_lock, &old_irql, "remove card");
list_for_each(tmp, &cards) {
card = list_entry(tmp, diva_card, list);
if (card->d.request == d->request) {
card->remove_in_progress = 1;
list_del(tmp);
break;
}
}
diva_os_leave_spin_lock(&api_lock, &old_irql, "remove card");
if (card) {
/*
* Detach CAPI. Sendf cannot call to CAPI any more.
* After detach no call to send_message() is done too.
*/
detach_capi_ctr(&card->capi_ctrl); detach_capi_ctr(&card->capi_ctrl);
clean_adapter(card->Id - 1);
DBG_TRC(("adapter remove, max_adapter=%d", max_adapter)); /*
card = card->next; * Now get API lock (to ensure stable state of LI tables)
* and update the adapter map/LI table.
*/
diva_os_enter_spin_lock(&api_lock, &old_irql, "remove card");
clean_adapter(card->Id - 1, &free_mem_q);
DBG_TRC(("DelAdapterMap (%d) -> (%d)",
ControllerMap[card->Id], card->Id))
ControllerMap[card->Id] = 0;
DBG_TRC(("adapter remove, max_adapter=%d",
max_adapter));
diva_os_leave_spin_lock(&api_lock, &old_irql, "remove card");
/* After releasing the lock, we can free the memory */
diva_os_free (0, card);
} }
card = cards; /* free queued memory areas */
while (card) { while ((link = diva_q_get_head(&free_mem_q))) {
last = card; diva_q_remove(&free_mem_q, link);
card = card->next; diva_os_free(0, link);
diva_os_free(0, last);
} }
diva_os_leave_spin_lock(&ll_lock, &old_irql, "remove cards");
} }
/* /*
* remove a card * remove cards
*/ */
static void divacapi_remove_card(DESCRIPTOR * d) static void DIVA_EXIT_FUNCTION divacapi_remove_cards(void)
{ {
diva_card *last; DESCRIPTOR d;
struct list_head *tmp;
diva_card *card; diva_card *card;
diva_os_spin_lock_magic_t old_irql; diva_os_spin_lock_magic_t old_irql;
diva_os_enter_spin_lock(&ll_lock, &old_irql, "remove card"); rescan:
last = card = cards; diva_os_enter_spin_lock(&api_lock, &old_irql, "remove cards");
while (card) { list_for_each(tmp, &cards) {
if (card->d.request == d->request) { card = list_entry(tmp, diva_card, list);
detach_capi_ctr(&card->capi_ctrl); diva_os_leave_spin_lock(&api_lock, &old_irql, "remove cards");
clean_adapter(card->Id - 1); d.request = card->d.request;
DBG_TRC( divacapi_remove_card(&d);
("DelAdapterMap (%d) -> (%d)", goto rescan;
ControllerMap[card->Id], card->Id))
ControllerMap[card->Id] = 0;
DBG_TRC(
("adapter remove, max_adapter=%d",
max_adapter));
if (card == last)
cards = card->next;
else
last->next = card->next;
diva_os_free(0, card);
break;
} }
last = card; diva_os_leave_spin_lock(&api_lock, &old_irql, "remove cards");
card = card->next;
}
diva_os_leave_spin_lock(&ll_lock, &old_irql, "remove card");
} }
/* /*
...@@ -500,10 +500,9 @@ static int diva_add_card(DESCRIPTOR * d) ...@@ -500,10 +500,9 @@ static int diva_add_card(DESCRIPTOR * d)
DIVA_CAPI_ADAPTER *a = NULL; DIVA_CAPI_ADAPTER *a = NULL;
IDI_SYNC_REQ sync_req; IDI_SYNC_REQ sync_req;
char serial[16]; char serial[16];
#if IMPLEMENT_LINE_INTERCONNECT2 void* mem_to_free;
LI_CONFIG *new_li_config_table; LI_CONFIG *new_li_config_table;
int j; int j;
#endif /* IMPLEMENT_LINE_INTERCONNECT2 */
if (!(card = (diva_card *) diva_os_malloc(0, sizeof(diva_card)))) { if (!(card = (diva_card *) diva_os_malloc(0, sizeof(diva_card)))) {
DBG_ERR(("diva_add_card: failed to allocate card struct.")) DBG_ERR(("diva_add_card: failed to allocate card struct."))
...@@ -529,7 +528,11 @@ static int diva_add_card(DESCRIPTOR * d) ...@@ -529,7 +528,11 @@ static int diva_add_card(DESCRIPTOR * d)
diva_os_free(0, card); diva_os_free(0, card);
return (0); return (0);
} }
diva_os_enter_spin_lock(&api_lock, &old_irql, "find id");
card->Id = find_free_id(); card->Id = find_free_id();
diva_os_leave_spin_lock(&api_lock, &old_irql, "find id");
strlcpy(ctrl->manu, M_COMPANY, sizeof(ctrl->manu)); strlcpy(ctrl->manu, M_COMPANY, sizeof(ctrl->manu));
ctrl->version.majorversion = 2; ctrl->version.majorversion = 2;
ctrl->version.minorversion = 0; ctrl->version.minorversion = 0;
...@@ -600,9 +603,7 @@ static int diva_add_card(DESCRIPTOR * d) ...@@ -600,9 +603,7 @@ static int diva_add_card(DESCRIPTOR * d)
#if IMPLEMENT_DTMF #if IMPLEMENT_DTMF
a->profile.Global_Options |= 0x8; a->profile.Global_Options |= 0x8;
#endif /* IMPLEMENT_DTMF */ #endif /* IMPLEMENT_DTMF */
#if (IMPLEMENT_LINE_INTERCONNECT || IMPLEMENT_LINE_INTERCONNECT2) a->profile.Global_Options |= 0x80; /* Line Interconnect */
a->profile.Global_Options |= 0x80;
#endif /* (IMPLEMENT_LINE_INTERCONNECT || IMPLEMENT_LINE_INTERCONNECT2) */
#if IMPLEMENT_ECHO_CANCELLER #if IMPLEMENT_ECHO_CANCELLER
a->profile.Global_Options |= 0x100; a->profile.Global_Options |= 0x100;
#endif /* IMPLEMENT_ECHO_CANCELLER */ #endif /* IMPLEMENT_ECHO_CANCELLER */
...@@ -620,25 +621,6 @@ static int diva_add_card(DESCRIPTOR * d) ...@@ -620,25 +621,6 @@ static int diva_add_card(DESCRIPTOR * d)
a->manufacturer_features = 0; a->manufacturer_features = 0;
} }
#if IMPLEMENT_LINE_INTERCONNECT
a->li_pri = (a->profile.Channels > 2);
if (a->li_pri) {
if (!(a->li_config.pri = (LI_CONFIG_PRI *) diva_os_malloc(0, sizeof(LI_CONFIG_PRI)))) {
DBG_ERR(("diva_add_card: failed alloc li_config.pri struct."))
memset(a, 0, sizeof(DIVA_CAPI_ADAPTER));
return (0);
}
memset(a->li_config.pri, 0, sizeof(LI_CONFIG_PRI));
} else
if (!(a->li_config.bri = (LI_CONFIG_BRI *) diva_os_malloc(0, sizeof(LI_CONFIG_BRI)))) {
DBG_ERR(("diva_add_card: failed alloc li_config.bri struct."))
memset(a, 0, sizeof(DIVA_CAPI_ADAPTER));
return (0);
}
memset(a->li_config.bri, 0, sizeof(LI_CONFIG_BRI));
}
#endif /* IMPLEMENT_LINE_INTERCONNECT */
#if IMPLEMENT_LINE_INTERCONNECT2
a->li_pri = (a->profile.Channels > 2); a->li_pri = (a->profile.Channels > 2);
a->li_channels = a->li_pri ? MIXER_CHANNELS_PRI : MIXER_CHANNELS_BRI; a->li_channels = a->li_pri ? MIXER_CHANNELS_PRI : MIXER_CHANNELS_BRI;
a->li_base = 0; a->li_base = 0;
...@@ -654,6 +636,10 @@ static int diva_add_card(DESCRIPTOR * d) ...@@ -654,6 +636,10 @@ static int diva_add_card(DESCRIPTOR * d)
memset(a, 0, sizeof(DIVA_CAPI_ADAPTER)); memset(a, 0, sizeof(DIVA_CAPI_ADAPTER));
return (0); return (0);
} }
/* Prevent access to line interconnect table in process update */
diva_os_enter_spin_lock(&api_lock, &old_irql, "add card");
j = 0; j = 0;
for (i = 0; i < k; i++) { for (i = 0; i < k; i++) {
if ((i >= a->li_base) && (i < a->li_base + a->li_channels)) if ((i >= a->li_base) && (i < a->li_base + a->li_channels))
...@@ -694,25 +680,26 @@ static int diva_add_card(DESCRIPTOR * d) ...@@ -694,25 +680,26 @@ static int diva_add_card(DESCRIPTOR * d)
} }
li_total_channels = k; li_total_channels = k;
if (li_config_table != NULL) mem_to_free = li_config_table;
diva_os_free(0, li_config_table);
li_config_table = new_li_config_table; li_config_table = new_li_config_table;
for (i = card->Id; i < max_adapter; i++) { for (i = card->Id; i < max_adapter; i++) {
if (adapter[i].request) if (adapter[i].request)
adapter[i].li_base += a->li_channels; adapter[i].li_base += a->li_channels;
} }
#endif /* IMPLEMENT_LINE_INTERCONNECT2 */
if (a == &adapter[max_adapter]) if (a == &adapter[max_adapter])
max_adapter++; max_adapter++;
diva_os_enter_spin_lock(&ll_lock, &old_irql, "add card"); list_add(&(card->list), &cards);
card->next = cards;
cards = card;
diva_os_leave_spin_lock(&ll_lock, &old_irql, "add card");
AutomaticLaw(a); AutomaticLaw(a);
diva_os_leave_spin_lock(&api_lock, &old_irql, "add card");
if (mem_to_free) {
diva_os_free (0, mem_to_free);
}
i = 0; i = 0;
while (i++ < 30) { while (i++ < 30) {
if (a->automatic_law > 3) if (a->automatic_law > 3)
...@@ -863,6 +850,7 @@ static void diva_release_appl(struct capi_ctr *ctrl, __u16 appl) ...@@ -863,6 +850,7 @@ static void diva_release_appl(struct capi_ctr *ctrl, __u16 appl)
{ {
diva_os_spin_lock_magic_t old_irql; diva_os_spin_lock_magic_t old_irql;
APPL *this = &application[appl - 1]; APPL *this = &application[appl - 1];
void *mem_to_free = NULL;
DBG_TRC(("application %d(%d) cleanup", this->Id, appl)) DBG_TRC(("application %d(%d) cleanup", this->Id, appl))
...@@ -874,13 +862,15 @@ static void diva_release_appl(struct capi_ctr *ctrl, __u16 appl) ...@@ -874,13 +862,15 @@ static void diva_release_appl(struct capi_ctr *ctrl, __u16 appl)
diva_os_enter_spin_lock(&api_lock, &old_irql, "release_appl"); diva_os_enter_spin_lock(&api_lock, &old_irql, "release_appl");
if (this->Id) { if (this->Id) {
CapiRelease(this->Id); CapiRelease(this->Id);
if (this->DataNCCI) mem_to_free = this->DataNCCI;
diva_os_free(0, this->DataNCCI);
this->DataNCCI = NULL; this->DataNCCI = NULL;
this->Id = 0; this->Id = 0;
} }
diva_os_leave_spin_lock(&api_lock, &old_irql, "release_appl"); diva_os_leave_spin_lock(&api_lock, &old_irql, "release_appl");
if (mem_to_free)
diva_os_free(0, mem_to_free);
} }
/* /*
...@@ -906,6 +896,11 @@ static u16 diva_send_message(struct capi_ctr *ctrl, ...@@ -906,6 +896,11 @@ static u16 diva_send_message(struct capi_ctr *ctrl,
} }
DBG_PRV1(("Write - appl = %d, cmd = 0x%x", this->Id, command)) DBG_PRV1(("Write - appl = %d, cmd = 0x%x", this->Id, command))
if (card->remove_in_progress) {
DBG_ERR(("CAPI_SEND_MSG - remove in progress!"))
return CAPI_REGOSRESOURCEERR;
}
if (!this->Id) { if (!this->Id) {
return CAPI_ILLAPPNR; return CAPI_ILLAPPNR;
} }
...@@ -1162,12 +1157,31 @@ static void remove_main_structs(void) ...@@ -1162,12 +1157,31 @@ static void remove_main_structs(void)
diva_os_free(0, mapped_msg); diva_os_free(0, mapped_msg);
} }
/*
* api_remove_start
*/
static void do_api_remove_start(void)
{
diva_os_spin_lock_magic_t old_irql;
int ret = 1, count = 100;
do {
diva_os_enter_spin_lock(&api_lock, &old_irql, "api remove start");
ret = api_remove_start();
diva_os_leave_spin_lock(&api_lock, &old_irql, "api remove start");
diva_os_sleep(10);
} while (ret && count--);
if (ret)
DBG_ERR(("could not remove signaling ID's"))
}
/* /*
* init * init
*/ */
int DIVA_INIT_FUNCTION init_capifunc(void) int DIVA_INIT_FUNCTION init_capifunc(void)
{ {
diva_os_initialize_spin_lock(&ll_lock, "capifunc");
diva_os_initialize_spin_lock(&api_lock, "capifunc"); diva_os_initialize_spin_lock(&api_lock, "capifunc");
memset(ControllerMap, 0, MAX_DESCRIPTORS + 1); memset(ControllerMap, 0, MAX_DESCRIPTORS + 1);
max_adapter = 0; max_adapter = 0;
...@@ -1175,12 +1189,16 @@ int DIVA_INIT_FUNCTION init_capifunc(void) ...@@ -1175,12 +1189,16 @@ int DIVA_INIT_FUNCTION init_capifunc(void)
if (!init_main_structs()) { if (!init_main_structs()) {
DBG_ERR(("init: failed to init main structs.")) DBG_ERR(("init: failed to init main structs."))
diva_os_destroy_spin_lock(&api_lock, "capifunc");
return (0); return (0);
} }
if (!divacapi_connect_didd()) { if (!divacapi_connect_didd()) {
DBG_ERR(("init: failed to connect to DIDD.")) DBG_ERR(("init: failed to connect to DIDD."))
do_api_remove_start();
divacapi_remove_cards();
remove_main_structs(); remove_main_structs();
diva_os_destroy_spin_lock(&api_lock, "capifunc");
return (0); return (0);
} }
...@@ -1192,21 +1210,9 @@ int DIVA_INIT_FUNCTION init_capifunc(void) ...@@ -1192,21 +1210,9 @@ int DIVA_INIT_FUNCTION init_capifunc(void)
*/ */
void DIVA_EXIT_FUNCTION finit_capifunc(void) void DIVA_EXIT_FUNCTION finit_capifunc(void)
{ {
int count = 100; do_api_remove_start();
word ret = 1;
while (ret && count--) {
ret = api_remove_start();
diva_os_sleep(10);
}
if (ret)
DBG_ERR(("could not remove signaling ID's"))
divacapi_disconnect_didd(); divacapi_disconnect_didd();
divacapi_remove_cards(); divacapi_remove_cards();
remove_main_structs(); remove_main_structs();
diva_os_destroy_spin_lock(&api_lock, "capifunc"); diva_os_destroy_spin_lock(&api_lock, "capifunc");
diva_os_destroy_spin_lock(&ll_lock, "capifunc");
} }
/* $Id: capifunc.h,v 1.10 2003/08/25 10:06:37 schindler Exp $ /* $Id: capifunc.h,v 1.11 2004/03/20 17:19:58 armin Exp $
* *
* ISDN interface module for Eicon active cards DIVA. * ISDN interface module for Eicon active cards DIVA.
* CAPI Interface common functions * CAPI Interface common functions
...@@ -24,8 +24,9 @@ ...@@ -24,8 +24,9 @@
extern char DRIVERRELEASE_CAPI[]; extern char DRIVERRELEASE_CAPI[];
typedef struct _diva_card { typedef struct _diva_card {
struct list_head list;
int remove_in_progress;
int Id; int Id;
struct _diva_card *next;
struct capi_ctr capi_ctrl; struct capi_ctr capi_ctrl;
DIVA_CAPI_ADAPTER *adapter; DIVA_CAPI_ADAPTER *adapter;
DESCRIPTOR d; DESCRIPTOR d;
......
/* $Id: divasmain.c,v 1.48 2004/02/24 17:46:28 armin Exp $ /* $Id: divasmain.c,v 1.51 2004/03/20 20:47:08 armin Exp $
* *
* Low level driver for Eicon DIVA Server ISDN cards. * Low level driver for Eicon DIVA Server ISDN cards.
* *
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
#include "diva_dma.h" #include "diva_dma.h"
#include "diva_pci.h" #include "diva_pci.h"
static char *main_revision = "$Revision: 1.48 $"; static char *main_revision = "$Revision: 1.51 $";
static int major; static int major;
...@@ -69,7 +69,7 @@ extern int divasfunc_init(int dbgmask); ...@@ -69,7 +69,7 @@ extern int divasfunc_init(int dbgmask);
extern void divasfunc_exit(void); extern void divasfunc_exit(void);
typedef struct _diva_os_thread_dpc { typedef struct _diva_os_thread_dpc {
struct work_struct divas_task; struct tasklet_struct divas_task;
struct work_struct trap_script_task; struct work_struct trap_script_task;
diva_os_soft_isr_t *psoft_isr; diva_os_soft_isr_t *psoft_isr;
int card_failed; int card_failed;
...@@ -552,7 +552,7 @@ void diva_os_remove_irq(void *context, byte irq) ...@@ -552,7 +552,7 @@ void diva_os_remove_irq(void *context, byte irq)
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
DPC framework implementation DPC framework implementation
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
static void diva_os_dpc_proc(void *context) static void diva_os_dpc_proc(unsigned long context)
{ {
diva_os_thread_dpc_t *psoft_isr = (diva_os_thread_dpc_t *) context; diva_os_thread_dpc_t *psoft_isr = (diva_os_thread_dpc_t *) context;
diva_os_soft_isr_t *pisr = psoft_isr->psoft_isr; diva_os_soft_isr_t *pisr = psoft_isr->psoft_isr;
...@@ -575,7 +575,7 @@ int diva_os_initialize_soft_isr(diva_os_soft_isr_t * psoft_isr, ...@@ -575,7 +575,7 @@ int diva_os_initialize_soft_isr(diva_os_soft_isr_t * psoft_isr,
psoft_isr->callback_context = callback_context; psoft_isr->callback_context = callback_context;
pdpc->psoft_isr = psoft_isr; pdpc->psoft_isr = psoft_isr;
INIT_WORK(&pdpc->trap_script_task, diva_adapter_trapped, pdpc); INIT_WORK(&pdpc->trap_script_task, diva_adapter_trapped, pdpc);
INIT_WORK(&pdpc->divas_task, diva_os_dpc_proc, pdpc); tasklet_init(&pdpc->divas_task, diva_os_dpc_proc, (unsigned long)pdpc);
return (0); return (0);
} }
...@@ -586,7 +586,7 @@ int diva_os_schedule_soft_isr(diva_os_soft_isr_t * psoft_isr) ...@@ -586,7 +586,7 @@ int diva_os_schedule_soft_isr(diva_os_soft_isr_t * psoft_isr)
diva_os_thread_dpc_t *pdpc = diva_os_thread_dpc_t *pdpc =
(diva_os_thread_dpc_t *) psoft_isr->object; (diva_os_thread_dpc_t *) psoft_isr->object;
schedule_work(&pdpc->divas_task); tasklet_schedule(&pdpc->divas_task);
} }
return (1); return (1);
...@@ -594,14 +594,22 @@ int diva_os_schedule_soft_isr(diva_os_soft_isr_t * psoft_isr) ...@@ -594,14 +594,22 @@ int diva_os_schedule_soft_isr(diva_os_soft_isr_t * psoft_isr)
int diva_os_cancel_soft_isr(diva_os_soft_isr_t * psoft_isr) int diva_os_cancel_soft_isr(diva_os_soft_isr_t * psoft_isr)
{ {
if (psoft_isr && psoft_isr->object) {
diva_os_thread_dpc_t *pdpc =
(diva_os_thread_dpc_t *) psoft_isr->object;
tasklet_kill(&pdpc->divas_task);
}
return (0); return (0);
} }
void diva_os_remove_soft_isr(diva_os_soft_isr_t * psoft_isr) void diva_os_remove_soft_isr(diva_os_soft_isr_t * psoft_isr)
{ {
if (psoft_isr && psoft_isr->object) { if (psoft_isr && psoft_isr->object) {
diva_os_thread_dpc_t *pdpc =
(diva_os_thread_dpc_t *) psoft_isr->object;
void *mem; void *mem;
tasklet_kill(&pdpc->divas_task);
flush_scheduled_work(); flush_scheduled_work();
mem = psoft_isr->object; mem = psoft_isr->object;
psoft_isr->object = 0; psoft_isr->object = 0;
......
/* $Id: platform.h,v 1.35 2003/12/05 18:45:05 armin Exp $ /* $Id: platform.h,v 1.37 2004/03/20 17:44:29 armin Exp $
* *
* platform.h * platform.h
* *
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/list.h>
#include <asm/types.h> #include <asm/types.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -332,7 +333,6 @@ diva_os_atomic_decrement(diva_os_atomic_t* pv) ...@@ -332,7 +333,6 @@ diva_os_atomic_decrement(diva_os_atomic_t* pv)
*/ */
#define NO_CORNETN #define NO_CORNETN
#define IMPLEMENT_DTMF 1 #define IMPLEMENT_DTMF 1
#define IMPLEMENT_LINE_INTERCONNECT2 1
#define IMPLEMENT_ECHO_CANCELLER 1 #define IMPLEMENT_ECHO_CANCELLER 1
#define IMPLEMENT_RTP 1 #define IMPLEMENT_RTP 1
#define IMPLEMENT_T38 1 #define IMPLEMENT_T38 1
...@@ -346,7 +346,6 @@ diva_os_atomic_decrement(diva_os_atomic_t* pv) ...@@ -346,7 +346,6 @@ diva_os_atomic_decrement(diva_os_atomic_t* pv)
#define IMPLEMENT_FAX_NONSTANDARD 1 #define IMPLEMENT_FAX_NONSTANDARD 1
#define VSWITCH_SUPPORT 1 #define VSWITCH_SUPPORT 1
#define IMPLEMENT_LINE_INTERCONNECT 0
#define IMPLEMENT_MARKED_OK_AFTER_FC 1 #define IMPLEMENT_MARKED_OK_AFTER_FC 1
#define DIVA_IDI_RX_DMA 1 #define DIVA_IDI_RX_DMA 1
......
/* /*
* Creation Date: <2003/03/14 20:54:13 samuel> * Creation Date: <2003/03/14 20:54:13 samuel>
* Time-stamp: <2003/03/15 18:55:53 samuel> * Time-stamp: <2004/03/20 14:20:59 samuel>
* *
* <therm_windtunnel.c> * <therm_windtunnel.c>
* *
* The G4 "windtunnel" has a single fan controlled by a * The G4 "windtunnel" has a single fan controlled by an
* DS1775 fan controller and an ADM1030 thermostat. * ADM1030 fan controller and a DS1775 thermostat.
* *
* The fan controller is equipped with a temperature sensor * The fan controller is equipped with a temperature sensor
* which measures the case temperature. The ADM censor * which measures the case temperature. The DS1775 sensor
* measures the CPU temperature. This driver tunes the * measures the CPU temperature. This driver tunes the
* behavior of the fan. It is based upon empirical observations * behavior of the fan. It is based upon empirical observations
* of the 'AppleFan' driver under OSX. * of the 'AppleFan' driver under Mac OS X.
* *
* WARNING: This driver has only been testen on Apple's * WARNING: This driver has only been testen on Apple's
* 1.25 MHz Dual G4 (March 03). Other machines might have * 1.25 MHz Dual G4 (March 03). It is tuned for a CPU
* a different thermal design. It is tuned for a CPU
* temperatur around 57 C. * temperatur around 57 C.
* *
* Copyright (C) 2003 Samuel Rydh (samuel@ibrium.se) * Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se)
* *
* Loosely based upon 'thermostat.c' written by Benjamin Herrenschmidt * Loosely based upon 'thermostat.c' written by Benjamin Herrenschmidt
* *
...@@ -38,50 +37,37 @@ ...@@ -38,50 +37,37 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/workqueue.h>
#include <asm/prom.h> #include <asm/prom.h>
#include <asm/machdep.h> #include <asm/machdep.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/sections.h> #include <asm/sections.h>
#include <asm/of_device.h>
MODULE_AUTHOR("Samuel Rydh <samuel@ibrium.se>");
MODULE_DESCRIPTION("Apple G4 (windtunnel) fan driver");
MODULE_LICENSE("GPL");
#define LOG_TEMP 0 /* continously log temperature */ #define LOG_TEMP 0 /* continously log temperature */
#define I2C_DRIVERID_G4FAN 0x9001 /* fixme */
#define THERMOSTAT_CLIENT_ID 1
#define FAN_CLIENT_ID 2
static int do_probe( struct i2c_adapter *adapter, int addr, int kind);
/* scan 0x48-0x4f (DS1775) and 0x2c-2x2f (ADM1030) */ /* scan 0x48-0x4f (DS1775) and 0x2c-2x2f (ADM1030) */
static unsigned short normal_i2c[] = { 0x49, 0x2c, I2C_CLIENT_END }; static unsigned short normal_i2c[] = { 0x49, 0x2c, I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { 0x48, 0x4f, 0x2c, 0x2f, I2C_CLIENT_END }; static unsigned short normal_i2c_range[] = { 0x48, 0x4f, 0x2c, 0x2f, I2C_CLIENT_END };
static struct work_struct poll_work;
I2C_CLIENT_INSMOD; I2C_CLIENT_INSMOD;
#define I2C_DRIVERID_G4FAN 0x9001 /* fixme */ static struct {
volatile int running;
#define THERMOSTAT_CLIENT_ID 1 struct completion completion;
#define FAN_CLIENT_ID 2 pid_t poll_task;
struct temp_range {
u8 high; /* start the fan */
u8 low; /* stop the fan */
};
struct apple_thermal_info {
u8 id; /* implementation ID */
u8 fan_count; /* number of fans */
u8 thermostat_count; /* number of thermostats */
u8 unused[5];
struct temp_range ranges[4]; /* temperature ranges (may be [])*/
};
static int do_detect( struct i2c_adapter *adapter, int addr, int kind); struct semaphore lock;
struct of_device *of_dev;
static struct {
struct i2c_client *thermostat; struct i2c_client *thermostat;
struct i2c_client *fan; struct i2c_client *fan;
int error;
struct timer_list timer;
int overheat_temp; /* 100% fan at this temp */ int overheat_temp; /* 100% fan at this temp */
int overheat_hyst; int overheat_hyst;
...@@ -95,37 +81,54 @@ static struct { ...@@ -95,37 +81,54 @@ static struct {
int r0, r1, r20, r23, r25; /* saved register */ int r0, r1, r20, r23, r25; /* saved register */
} x; } x;
#define T(x,y) (((x)<<8) | (y)*0x100/10 )
static struct { static struct {
int fan_down_setting;
int temp; int temp;
int fan_setting; int fan_up_setting;
} fan_up_table[] = { } fan_table[] = {
{ 0x0000, 11 }, /* min fan */ { 11, T(0,0), 11 }, /* min fan */
{ 0x3900, 8 }, /* 57.0 C */ { 11, T(55,0), 11 },
{ 0x3a4a, 7 }, /* 58.3 C */ { 6, T(55,3), 11 },
{ 0x3ad3, 6 }, /* 58.8 C */ { 7, T(56,0), 11 },
{ 0x3b3c, 5 }, /* 59.2 C */ { 8, T(57,0), 8 },
{ 0x3b94, 4 }, /* 59.6 C */ { 7, T(58,3), 7 },
{ 0x3be3, 3 }, /* 58.9 C */ { 6, T(58,8), 6 },
{ 0x3c29, 2 }, /* 59.2 C */ { 5, T(59,2), 5 },
{ 0xffff, 1 } /* on fire */ { 4, T(59,6), 4 },
}; { 3, T(59,9), 3 },
static struct { { 2, T(60,1), 2 },
int temp; { 1, 0xfffff, 1 } /* on fire */
int fan_setting;
} fan_down_table[] = {
{ 0x3700, 11 }, /* 55.0 C */
{ 0x374a, 6 },
{ 0x3800, 7 }, /* 56.0 C */
{ 0x3900, 8 }, /* 57.0 C */
{ 0x3a4a, 7 }, /* 58.3 C */
{ 0x3ad3, 6 }, /* 58.8 C */
{ 0x3b3c, 5 }, /* 59.2 C */
{ 0x3b94, 4 }, /* 58.9 C */
{ 0x3be3, 3 }, /* 58.9 C */
{ 0x3c29, 2 }, /* 59.2 C */
{ 0xffff, 1 }
}; };
static void
print_temp( const char *s, int temp )
{
printk("%s%d.%d C", s ? s : "", temp>>8, (temp & 255)*10/256 );
}
static ssize_t
show_cpu_temperature( struct device *dev, char *buf )
{
return sprintf(buf, "%d.%d\n", x.temp>>8, (x.temp & 255)*10/256 );
}
static ssize_t
show_case_temperature( struct device *dev, char *buf )
{
return sprintf(buf, "%d.%d\n", x.casetemp>>8, (x.casetemp & 255)*10/256 );
}
static DEVICE_ATTR(cpu_temperature, S_IRUGO, show_cpu_temperature, NULL );
static DEVICE_ATTR(case_temperature, S_IRUGO, show_case_temperature, NULL );
/************************************************************************/
/* controller thread */
/************************************************************************/
static int static int
write_reg( struct i2c_client *cl, int reg, int data, int len ) write_reg( struct i2c_client *cl, int reg, int data, int len )
{ {
...@@ -159,37 +162,32 @@ read_reg( struct i2c_client *cl, int reg, int len ) ...@@ -159,37 +162,32 @@ read_reg( struct i2c_client *cl, int reg, int len )
return (len == 2)? ((unsigned int)buf[0] << 8) | buf[1] : buf[0]; return (len == 2)? ((unsigned int)buf[0] << 8) | buf[1] : buf[0];
} }
static void
print_temp( const char *s, int temp )
{
printk("%s%d.%d C", s ? s : "", temp>>8, (temp & 255)*10/256 );
}
static void static void
tune_fan( int fan_setting ) tune_fan( int fan_setting )
{ {
int val = (fan_setting << 3) | 7; int val = (fan_setting << 3) | 7;
x.fan_level = fan_setting;
//write_reg( x.fan, 0x24, val, 1 ); /* write_reg( x.fan, 0x24, val, 1 ); */
write_reg( x.fan, 0x25, val, 1 ); write_reg( x.fan, 0x25, val, 1 );
write_reg( x.fan, 0x20, 0, 1 ); write_reg( x.fan, 0x20, 0, 1 );
print_temp("CPU-temp: ", x.temp ); print_temp("CPU-temp: ", x.temp );
if( x.casetemp ) if( x.casetemp )
print_temp(", Case: ", x.casetemp ); print_temp(", Case: ", x.casetemp );
printk(" Tuning fan: %d (%02x)\n", fan_setting, val ); printk(", Fan: %d (tuned %+d)\n", 11-fan_setting, x.fan_level-fan_setting );
x.fan_level = fan_setting;
} }
static void static void
poll_temp( void *param ) poll_temp( void )
{ {
int temp = read_reg( x.thermostat, 0, 2 ); int temp, i, level, casetemp;
int i, level, casetemp;
temp = read_reg( x.thermostat, 0, 2 );
/* this actually occurs when the computer is loaded */ /* this actually occurs when the computer is loaded */
if( temp < 0 ) if( temp < 0 )
goto out; return;
casetemp = read_reg(x.fan, 0x0b, 1) << 8; casetemp = read_reg(x.fan, 0x0b, 1) << 8;
casetemp |= (read_reg(x.fan, 0x06, 1) & 0x7) << 5; casetemp |= (read_reg(x.fan, 0x06, 1) & 0x7) << 5;
...@@ -197,37 +195,117 @@ poll_temp( void *param ) ...@@ -197,37 +195,117 @@ poll_temp( void *param )
if( LOG_TEMP && x.temp != temp ) { if( LOG_TEMP && x.temp != temp ) {
print_temp("CPU-temp: ", temp ); print_temp("CPU-temp: ", temp );
print_temp(", Case: ", casetemp ); print_temp(", Case: ", casetemp );
printk(", Fan: %d\n", x.fan_level ); printk(", Fan: %d\n", 11-x.fan_level );
} }
x.temp = temp; x.temp = temp;
x.casetemp = casetemp; x.casetemp = casetemp;
level = -1; level = -1;
for( i=0; (temp & 0xffff) > fan_down_table[i].temp ; i++ ) for( i=0; (temp & 0xffff) > fan_table[i].temp ; i++ )
; ;
if( i < x.downind ) if( i < x.downind )
level = fan_down_table[i].fan_setting; level = fan_table[i].fan_down_setting;
x.downind = i; x.downind = i;
for( i=0; (temp & 0xfffe) >= fan_up_table[i+1].temp ; i++ ) for( i=0; (temp & 0xffff) >= fan_table[i+1].temp ; i++ )
; ;
if( x.upind < i ) if( x.upind < i )
level = fan_up_table[i].fan_setting; level = fan_table[i].fan_up_setting;
x.upind = i; x.upind = i;
if( level >= 0 ) if( level >= 0 )
tune_fan( level ); tune_fan( level );
out: }
x.timer.expires = jiffies + 8*HZ;
add_timer( &x.timer );
static void
setup_hardware( void )
{
int val;
/* save registers (if we unload the module) */
x.r0 = read_reg( x.fan, 0x00, 1 );
x.r1 = read_reg( x.fan, 0x01, 1 );
x.r20 = read_reg( x.fan, 0x20, 1 );
x.r23 = read_reg( x.fan, 0x23, 1 );
x.r25 = read_reg( x.fan, 0x25, 1 );
/* improve measurement resolution (convergence time 1.5s) */
if( (val=read_reg(x.thermostat, 1, 1)) >= 0 ) {
val |= 0x60;
if( write_reg( x.thermostat, 1, val, 1 ) )
printk("Failed writing config register\n");
}
/* disable interrupts and TAC input */
write_reg( x.fan, 0x01, 0x01, 1 );
/* enable filter */
write_reg( x.fan, 0x23, 0x91, 1 );
/* remote temp. controls fan */
write_reg( x.fan, 0x00, 0x95, 1 );
/* The thermostat (which besides measureing temperature controls
* has a THERM output which puts the fan on 100%) is usually
* set to kick in at 80 C (chip default). We reduce this a bit
* to be on the safe side (OSX doesn't)...
*/
if( x.overheat_temp == (80 << 8) ) {
x.overheat_temp = 65 << 8;
x.overheat_hyst = 60 << 8;
write_reg( x.thermostat, 2, x.overheat_hyst, 2 );
write_reg( x.thermostat, 3, x.overheat_temp, 2 );
print_temp("Reducing overheating limit to ", x.overheat_temp );
print_temp(" (Hyst: ", x.overheat_hyst );
printk(")\n");
}
/* set an initial fan setting */
x.downind = 0xffff;
x.upind = -1;
/* tune_fan( fan_up_table[x.upind].fan_setting ); */
device_create_file( &x.of_dev->dev, &dev_attr_cpu_temperature );
device_create_file( &x.of_dev->dev, &dev_attr_case_temperature );
} }
static void static void
schedule_poll( unsigned long t ) restore_regs( void )
{
device_remove_file( &x.of_dev->dev, &dev_attr_cpu_temperature );
device_remove_file( &x.of_dev->dev, &dev_attr_case_temperature );
write_reg( x.fan, 0x01, x.r1, 1 );
write_reg( x.fan, 0x20, x.r20, 1 );
write_reg( x.fan, 0x23, x.r23, 1 );
write_reg( x.fan, 0x25, x.r25, 1 );
write_reg( x.fan, 0x00, x.r0, 1 );
}
static int
control_loop( void *dummy )
{ {
schedule_work(&poll_work); daemonize("g4fand");
down( &x.lock );
setup_hardware();
while( x.running ) {
up( &x.lock );
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout( 8*HZ );
down( &x.lock );
poll_temp();
}
restore_regs();
up( &x.lock );
complete_and_exit( &x.completion, 0 );
} }
/************************************************************************/ /************************************************************************/
/* i2c probing and setup */ /* i2c probing and setup */
/************************************************************************/ /************************************************************************/
...@@ -235,7 +313,20 @@ schedule_poll( unsigned long t ) ...@@ -235,7 +313,20 @@ schedule_poll( unsigned long t )
static int static int
do_attach( struct i2c_adapter *adapter ) do_attach( struct i2c_adapter *adapter )
{ {
return i2c_probe( adapter, &addr_data, &do_detect ); int ret = 0;
if( strncmp(adapter->name, "uni-n", 5) )
return 0;
if( !x.running ) {
ret = i2c_probe( adapter, &addr_data, &do_probe );
if( x.thermostat && x.fan ) {
x.running = 1;
init_completion( &x.completion );
x.poll_task = kernel_thread( control_loop, NULL, SIGCHLD | CLONE_KERNEL );
}
}
return ret;
} }
static int static int
...@@ -243,13 +334,23 @@ do_detach( struct i2c_client *client ) ...@@ -243,13 +334,23 @@ do_detach( struct i2c_client *client )
{ {
int err; int err;
printk("do_detach: id %d\n", client->id ); if( (err=i2c_detach_client(client)) )
if( (err=i2c_detach_client(client)) ) { printk(KERN_ERR "failed to detach thermostat client\n");
printk("failed to detach thermostat client\n"); else {
return err; if( x.running ) {
x.running = 0;
wait_for_completion( &x.completion );
}
if( client == x.thermostat )
x.thermostat = NULL;
else if( client == x.fan )
x.fan = NULL;
else {
printk(KERN_ERR "g4fan: bad client\n");
} }
kfree( client ); kfree( client );
return 0; }
return err;
} }
static struct i2c_driver g4fan_driver = { static struct i2c_driver g4fan_driver = {
...@@ -262,24 +363,21 @@ static struct i2c_driver g4fan_driver = { ...@@ -262,24 +363,21 @@ static struct i2c_driver g4fan_driver = {
}; };
static int static int
detect_fan( struct i2c_client *cl ) attach_fan( struct i2c_client *cl )
{ {
if( x.fan )
goto out;
/* check that this is an ADM1030 */ /* check that this is an ADM1030 */
if( read_reg(cl, 0x3d, 1) != 0x30 || read_reg(cl, 0x3e, 1) != 0x41 ) if( read_reg(cl, 0x3d, 1) != 0x30 || read_reg(cl, 0x3e, 1) != 0x41 )
goto out; goto out;
printk("ADM1030 fan controller detected at %02x\n", cl->addr ); printk("ADM1030 fan controller [@%02x]\n", cl->addr );
if( x.fan ) {
x.error |= 2;
goto out;
}
x.fan = cl;
cl->id = FAN_CLIENT_ID; cl->id = FAN_CLIENT_ID;
strncpy( cl->name, "ADM1030 fan controller", sizeof(cl->name) ); strlcpy( cl->name, "ADM1030 fan controller", sizeof(cl->name) );
if( i2c_attach_client( cl ) ) if( !i2c_attach_client(cl) )
goto out; x.fan = cl;
return 0;
out: out:
if( cl != x.fan ) if( cl != x.fan )
kfree( cl ); kfree( cl );
...@@ -287,10 +385,13 @@ detect_fan( struct i2c_client *cl ) ...@@ -287,10 +385,13 @@ detect_fan( struct i2c_client *cl )
} }
static int static int
detect_thermostat( struct i2c_client *cl ) attach_thermostat( struct i2c_client *cl )
{ {
int hyst_temp, os_temp, temp; int hyst_temp, os_temp, temp;
if( x.thermostat )
goto out;
if( (temp=read_reg(cl, 0, 2)) < 0 ) if( (temp=read_reg(cl, 0, 2)) < 0 )
goto out; goto out;
...@@ -302,44 +403,37 @@ detect_thermostat( struct i2c_client *cl ) ...@@ -302,44 +403,37 @@ detect_thermostat( struct i2c_client *cl )
if( hyst_temp < 0 || os_temp < 0 ) if( hyst_temp < 0 || os_temp < 0 )
goto out; goto out;
printk("DS1775 digital thermometer detected at %02x\n", cl->addr ); printk("DS1775 digital thermometer [@%02x]\n", cl->addr );
print_temp("Temp: ", temp ); print_temp("Temp: ", temp );
print_temp(" Hyst: ", hyst_temp ); print_temp(" Hyst: ", hyst_temp );
print_temp(" OS: ", os_temp ); print_temp(" OS: ", os_temp );
printk("\n"); printk("\n");
if( x.thermostat ) {
x.error |= 1;
goto out;
}
x.temp = temp; x.temp = temp;
x.thermostat = cl;
x.overheat_temp = os_temp; x.overheat_temp = os_temp;
x.overheat_hyst = hyst_temp; x.overheat_hyst = hyst_temp;
cl->id = THERMOSTAT_CLIENT_ID; cl->id = THERMOSTAT_CLIENT_ID;
strncpy( cl->name, "DS1775 thermostat", sizeof(cl->name) ); strlcpy( cl->name, "DS1775 thermostat", sizeof(cl->name) );
if( i2c_attach_client( cl ) ) if( !i2c_attach_client(cl) )
goto out; x.thermostat = cl;
return 0;
out: out:
if( cl != x.thermostat )
kfree( cl ); kfree( cl );
return 0; return 0;
} }
static int static int
do_detect( struct i2c_adapter *adapter, int addr, int kind ) do_probe( struct i2c_adapter *adapter, int addr, int kind )
{ {
struct i2c_client *cl; struct i2c_client *cl;
if( strncmp(adapter->name, "uni-n", 5) )
return 0;
if( !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA if( !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA
| I2C_FUNC_SMBUS_WRITE_BYTE) ) | I2C_FUNC_SMBUS_WRITE_BYTE) )
return 0; return 0;
if( !(cl=kmalloc( sizeof(struct i2c_client), GFP_KERNEL )) ) if( !(cl=kmalloc(sizeof(*cl), GFP_KERNEL)) )
return -ENOMEM; return -ENOMEM;
memset( cl, 0, sizeof(struct i2c_client) ); memset( cl, 0, sizeof(struct i2c_client) );
...@@ -349,108 +443,94 @@ do_detect( struct i2c_adapter *adapter, int addr, int kind ) ...@@ -349,108 +443,94 @@ do_detect( struct i2c_adapter *adapter, int addr, int kind )
cl->flags = 0; cl->flags = 0;
if( addr < 0x48 ) if( addr < 0x48 )
return detect_fan( cl ); return attach_fan( cl );
return detect_thermostat( cl ); return attach_thermostat( cl );
} }
#define PRINT_REG( r ) printk("reg %02x = %02x\n", r, read_reg(x.fan, r, 1) )
/************************************************************************/
/* initialization / cleanup */
/************************************************************************/
static int
therm_of_probe( struct of_device *dev, const struct of_match *match )
{
return i2c_add_driver( &g4fan_driver );
}
static int
therm_of_remove( struct of_device *dev )
{
return i2c_del_driver( &g4fan_driver );
}
static struct of_match therm_of_match[] = {{
.name = "fan",
.type = OF_ANY_MATCH,
.compatible = "adm1030"
}, {}
};
static struct of_platform_driver therm_of_driver = {
.name = "temperature",
.match_table = therm_of_match,
.probe = therm_of_probe,
.remove = therm_of_remove,
};
struct apple_thermal_info {
u8 id; /* implementation ID */
u8 fan_count; /* number of fans */
u8 thermostat_count; /* number of thermostats */
u8 unused;
};
static int __init static int __init
g4fan_init( void ) g4fan_init( void )
{ {
struct apple_thermal_info *info; struct apple_thermal_info *info;
struct device_node *np; struct device_node *np;
int ret, val;
np = of_find_node_by_name(NULL, "power-mgt"); init_MUTEX( &x.lock );
if (np == NULL)
if( !(np=of_find_node_by_name(NULL, "power-mgt")) )
return -ENODEV; return -ENODEV;
info = (struct apple_thermal_info*)get_property(np, "thermal-info", NULL); info = (struct apple_thermal_info*)get_property(np, "thermal-info", NULL);
of_node_put(np); of_node_put(np);
if (info == NULL)
if( !info || !machine_is_compatible("PowerMac3,6") )
return -ENODEV; return -ENODEV;
/* check for G4 "Windtunnel" SMP */
if( machine_is_compatible("PowerMac3,6") ) {
if( info->id != 3 ) { if( info->id != 3 ) {
printk(KERN_ERR "g4fan: design id %d unknown\n", info->id); printk(KERN_ERR "therm_windtunnel: unsupported thermal design %d\n", info->id );
return -ENODEV; return -ENODEV;
} }
} else { if( !(np=of_find_node_by_name(NULL, "fan")) )
printk(KERN_ERR "g4fan: unsupported machine type\n");
return -ENODEV; return -ENODEV;
} x.of_dev = of_platform_device_create( np, "temperature" );
if( (ret=i2c_add_driver(&g4fan_driver)) ) of_node_put( np );
return ret;
if( !x.thermostat || !x.fan ) { if( !x.of_dev ) {
i2c_del_driver(&g4fan_driver ); printk(KERN_ERR "Can't register fan controller!\n");
return -ENODEV; return -ENODEV;
} }
/* save registers (if we unload the module) */ of_register_driver( &therm_of_driver );
x.r0 = read_reg( x.fan, 0x00, 1 );
x.r1 = read_reg( x.fan, 0x01, 1 );
x.r20 = read_reg( x.fan, 0x20, 1 );
x.r23 = read_reg( x.fan, 0x23, 1 );
x.r25 = read_reg( x.fan, 0x25, 1 );
/* improve measurement resolution (convergence time 1.5s) */
if( (val=read_reg( x.thermostat, 1, 1 )) >= 0 ) {
val |= 0x60;
if( write_reg( x.thermostat, 1, val, 1 ) )
printk("Failed writing config register\n");
}
/* disable interrupts and TAC input */
write_reg( x.fan, 0x01, 0x01, 1 );
/* enable filter */
write_reg( x.fan, 0x23, 0x91, 1 );
/* remote temp. controls fan */
write_reg( x.fan, 0x00, 0x95, 1 );
/* The thermostat (which besides measureing temperature controls
* has a THERM output which puts the fan on 100%) is usually
* set to kick in at 80 C (chip default). We reduce this a bit
* to be on the safe side (OSX doesn't)...
*/
if( x.overheat_temp == (80 << 8) ) {
x.overheat_temp = 65 << 8;
x.overheat_hyst = 60 << 8;
write_reg( x.thermostat, 2, x.overheat_hyst, 2 );
write_reg( x.thermostat, 3, x.overheat_temp, 2 );
print_temp("Reducing overheating limit to ", x.overheat_temp );
print_temp(" (Hyst: ", x.overheat_hyst );
printk(")\n");
}
/* set an initial fan setting */
x.upind = x.downind = 1;
tune_fan( fan_up_table[x.upind].fan_setting );
INIT_WORK(&poll_work, poll_temp, NULL);
init_timer( &x.timer );
x.timer.expires = jiffies + 8*HZ;
x.timer.function = schedule_poll;
add_timer( &x.timer );
return 0; return 0;
} }
static void __exit static void __exit
g4fan_exit( void ) g4fan_exit( void )
{ {
del_timer( &x.timer ); of_unregister_driver( &therm_of_driver );
write_reg( x.fan, 0x01, x.r1, 1 ); if( x.of_dev )
write_reg( x.fan, 0x20, x.r20, 1 ); of_device_unregister( x.of_dev );
write_reg( x.fan, 0x23, x.r23, 1 );
write_reg( x.fan, 0x25, x.r25, 1 );
write_reg( x.fan, 0x00, x.r0, 1 );
i2c_del_driver( &g4fan_driver );
} }
module_init(g4fan_init); module_init(g4fan_init);
module_exit(g4fan_exit); module_exit(g4fan_exit);
MODULE_AUTHOR("Samuel Rydh <samuel@ibrium.se>");
MODULE_DESCRIPTION("Apple G4 (windtunnel) fan controller");
MODULE_LICENSE("GPL");
...@@ -1280,12 +1280,17 @@ mptbase_probe(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -1280,12 +1280,17 @@ mptbase_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return r; return r;
} }
#if 0
/* broken because some code assumes that multiple calls
to pci_alloc_consistent return data in the same 4GB segment.
This cannot work on machines with enough memory. */
if (!pci_set_consistent_dma_mask(pdev, mask)) if (!pci_set_consistent_dma_mask(pdev, mask))
dprintk((KERN_INFO MYNAM dprintk((KERN_INFO MYNAM
": Using 64 bit consistent mask\n")); ": Using 64 bit consistent mask\n"));
else else
dprintk((KERN_INFO MYNAM dprintk((KERN_INFO MYNAM
": Not using 64 bit consistent mask\n")); ": Not using 64 bit consistent mask\n"));
#endif
ioc = kmalloc(sizeof(MPT_ADAPTER), GFP_ATOMIC); ioc = kmalloc(sizeof(MPT_ADAPTER), GFP_ATOMIC);
if (ioc == NULL) { if (ioc == NULL) {
......
...@@ -1256,7 +1256,6 @@ sg_cmd_done(Scsi_Cmnd * SCpnt) ...@@ -1256,7 +1256,6 @@ sg_cmd_done(Scsi_Cmnd * SCpnt)
SRpnt->sr_request->rq_disk = NULL; /* "sg" _disowns_ request blk */ SRpnt->sr_request->rq_disk = NULL; /* "sg" _disowns_ request blk */
srp->my_cmdp = NULL; srp->my_cmdp = NULL;
srp->done = 1;
SCSI_LOG_TIMEOUT(4, printk("sg_cmd_done: %s, pack_id=%d, res=0x%x\n", SCSI_LOG_TIMEOUT(4, printk("sg_cmd_done: %s, pack_id=%d, res=0x%x\n",
sdp->disk->disk_name, srp->header.pack_id, (int) SRpnt->sr_result)); sdp->disk->disk_name, srp->header.pack_id, (int) SRpnt->sr_result));
...@@ -1312,8 +1311,9 @@ sg_cmd_done(Scsi_Cmnd * SCpnt) ...@@ -1312,8 +1311,9 @@ sg_cmd_done(Scsi_Cmnd * SCpnt)
} }
if (sfp && srp) { if (sfp && srp) {
/* Now wake up any sg_read() that is waiting for this packet. */ /* Now wake up any sg_read() that is waiting for this packet. */
wake_up_interruptible(&sfp->read_wait);
kill_fasync(&sfp->async_qp, SIGPOLL, POLL_IN); kill_fasync(&sfp->async_qp, SIGPOLL, POLL_IN);
srp->done = 1;
wake_up_interruptible(&sfp->read_wait);
} }
} }
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support
*/ */
static char *verstr = "20040226"; static char *verstr = "20040318";
#include <linux/module.h> #include <linux/module.h>
...@@ -4193,20 +4193,25 @@ CLASS_DEVICE_ATTR(default_compression, S_IRUGO, st_defcompression_show, NULL); ...@@ -4193,20 +4193,25 @@ CLASS_DEVICE_ATTR(default_compression, S_IRUGO, st_defcompression_show, NULL);
static void do_create_class_files(Scsi_Tape *STp, int dev_num, int mode) static void do_create_class_files(Scsi_Tape *STp, int dev_num, int mode)
{ {
int rew, error; int i, rew, error;
char name[10];
struct class_device *st_class_member; struct class_device *st_class_member;
if (!st_sysfs_class) if (!st_sysfs_class)
return; return;
for (rew=0; rew < 2; rew++) { for (rew=0; rew < 2; rew++) {
/* Make sure that the minor numbers corresponding to the four
first modes always get the same names */
i = mode << (4 - ST_NBR_MODE_BITS);
snprintf(name, 10, "%s%s%s", rew ? "n" : "",
STp->disk->disk_name, st_formats[i]);
st_class_member = st_class_member =
class_simple_device_add(st_sysfs_class, class_simple_device_add(st_sysfs_class,
MKDEV(SCSI_TAPE_MAJOR, MKDEV(SCSI_TAPE_MAJOR,
TAPE_MINOR(dev_num, mode, rew)), TAPE_MINOR(dev_num, mode, rew)),
&STp->device->sdev_gendev, "%s", &STp->device->sdev_gendev, "%s", name);
STp->modes[mode].cdevs[rew]->kobj.name); if (IS_ERR(st_class_member)) {
if (!st_class_member) {
printk(KERN_WARNING "st%d: class_simple_device_add failed\n", printk(KERN_WARNING "st%d: class_simple_device_add failed\n",
dev_num); dev_num);
goto out; goto out;
......
...@@ -550,7 +550,7 @@ static int do_open(struct block_device *bdev, struct file *file) ...@@ -550,7 +550,7 @@ static int do_open(struct block_device *bdev, struct file *file)
{ {
struct module *owner = NULL; struct module *owner = NULL;
struct gendisk *disk; struct gendisk *disk;
int ret = -ENXIO; int ret = -ENODEV;
int part; int part;
file->f_mapping = bdev->bd_inode->i_mapping; file->f_mapping = bdev->bd_inode->i_mapping;
...@@ -563,6 +563,7 @@ static int do_open(struct block_device *bdev, struct file *file) ...@@ -563,6 +563,7 @@ static int do_open(struct block_device *bdev, struct file *file)
} }
owner = disk->fops->owner; owner = disk->fops->owner;
ret = -ENXIO;
down(&bdev->bd_sem); down(&bdev->bd_sem);
if (!bdev->bd_openers) { if (!bdev->bd_openers) {
bdev->bd_disk = disk; bdev->bd_disk = disk;
......
...@@ -1004,7 +1004,8 @@ int do_sysctl_strategy (ctl_table *table, ...@@ -1004,7 +1004,8 @@ int do_sysctl_strategy (ctl_table *table,
* zero, proceed with automatic r/w */ * zero, proceed with automatic r/w */
if (table->data && table->maxlen) { if (table->data && table->maxlen) {
if (oldval && oldlenp) { if (oldval && oldlenp) {
get_user(len, oldlenp); if (get_user(len, oldlenp))
return -EFAULT;
if (len) { if (len) {
if (len > table->maxlen) if (len > table->maxlen)
len = table->maxlen; len = table->maxlen;
...@@ -1303,7 +1304,7 @@ int proc_dostring(ctl_table *table, int write, struct file *filp, ...@@ -1303,7 +1304,7 @@ int proc_dostring(ctl_table *table, int write, struct file *filp,
len = 0; len = 0;
p = buffer; p = buffer;
while (len < *lenp) { while (len < *lenp) {
if(get_user(c, p++)) if (get_user(c, p++))
return -EFAULT; return -EFAULT;
if (c == 0 || c == '\n') if (c == 0 || c == '\n')
break; break;
...@@ -1470,7 +1471,7 @@ static int do_proc_dointvec(ctl_table *table, int write, struct file *filp, ...@@ -1470,7 +1471,7 @@ static int do_proc_dointvec(ctl_table *table, int write, struct file *filp,
p = (char *) buffer; p = (char *) buffer;
while (left) { while (left) {
char c; char c;
if(get_user(c, p++)) if (get_user(c, p++))
return -EFAULT; return -EFAULT;
if (!isspace(c)) if (!isspace(c))
break; break;
...@@ -1705,7 +1706,7 @@ static int do_proc_doulongvec_minmax(ctl_table *table, int write, ...@@ -1705,7 +1706,7 @@ static int do_proc_doulongvec_minmax(ctl_table *table, int write,
p = (char *) buffer; p = (char *) buffer;
while (left) { while (left) {
char c; char c;
if(get_user(c, p++)) if (get_user(c, p++))
return -EFAULT; return -EFAULT;
if (!isspace(c)) if (!isspace(c))
break; break;
...@@ -1930,7 +1931,7 @@ int sysctl_string(ctl_table *table, int __user *name, int nlen, ...@@ -1930,7 +1931,7 @@ int sysctl_string(ctl_table *table, int __user *name, int nlen,
return -ENOTDIR; return -ENOTDIR;
if (oldval && oldlenp) { if (oldval && oldlenp) {
if(get_user(len, oldlenp)) if (get_user(len, oldlenp))
return -EFAULT; return -EFAULT;
if (len) { if (len) {
l = strlen(table->data); l = strlen(table->data);
...@@ -1987,7 +1988,8 @@ int sysctl_intvec(ctl_table *table, int __user *name, int nlen, ...@@ -1987,7 +1988,8 @@ int sysctl_intvec(ctl_table *table, int __user *name, int nlen,
for (i = 0; i < length; i++) { for (i = 0; i < length; i++) {
int value; int value;
get_user(value, vec + i); if (get_user(value, vec + i))
return -EFAULT;
if (min && value < min[i]) if (min && value < min[i])
return -EINVAL; return -EINVAL;
if (max && value > max[i]) if (max && value > max[i])
......
...@@ -685,8 +685,10 @@ DEBG("<stor"); ...@@ -685,8 +685,10 @@ DEBG("<stor");
} }
/*
STATIC int inflate_fixed(void) * We use `noinline' here to prevent gcc-3.5 from using too much stack space
*/
STATIC int noinline inflate_fixed(void)
/* decompress an inflated type 1 (fixed Huffman codes) block. We should /* decompress an inflated type 1 (fixed Huffman codes) block. We should
either replace this with a custom decoder, or at least precompute the either replace this with a custom decoder, or at least precompute the
Huffman tables. */ Huffman tables. */
...@@ -739,8 +741,10 @@ DEBG("<fix"); ...@@ -739,8 +741,10 @@ DEBG("<fix");
} }
/*
STATIC int inflate_dynamic(void) * We use `noinline' here to prevent gcc-3.5 from using too much stack space
*/
STATIC int noinline inflate_dynamic(void)
/* decompress an inflated type 2 (dynamic Huffman codes) block. */ /* decompress an inflated type 2 (dynamic Huffman codes) block. */
{ {
int i; /* temporary variables */ int i; /* temporary variables */
......
...@@ -237,7 +237,7 @@ sys_mprotect(unsigned long start, size_t len, unsigned long prot) ...@@ -237,7 +237,7 @@ sys_mprotect(unsigned long start, size_t len, unsigned long prot)
len = PAGE_ALIGN(len); len = PAGE_ALIGN(len);
end = start + len; end = start + len;
if (end < start) if (end < start)
return -EINVAL; return -ENOMEM;
if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC | PROT_SEM)) if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC | PROT_SEM))
return -EINVAL; return -EINVAL;
if (end == start) if (end == start)
......
...@@ -576,7 +576,7 @@ static void __slab_error(const char *function, kmem_cache_t *cachep, char *msg) ...@@ -576,7 +576,7 @@ static void __slab_error(const char *function, kmem_cache_t *cachep, char *msg)
* Add the CPU number into the expiry time to minimize the possibility of the * Add the CPU number into the expiry time to minimize the possibility of the
* CPUs getting into lockstep and contending for the global cache chain lock. * CPUs getting into lockstep and contending for the global cache chain lock.
*/ */
static void __init start_cpu_timer(int cpu) static void __devinit start_cpu_timer(int cpu)
{ {
struct timer_list *rt = &per_cpu(reap_timers, cpu); struct timer_list *rt = &per_cpu(reap_timers, cpu);
......
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