* Re: powerpc stacktrace and lockdep support
From: Johannes Berg @ 2007-07-04 22:40 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: linuxppc-dev
In-Reply-To: <20070630085808.GA15386@lst.de>
[-- Attachment #1: Type: text/plain, Size: 820 bytes --]
On Sat, 2007-06-30 at 10:58 +0200, Christoph Hellwig wrote:
> On Thu, Jun 28, 2007 at 06:20:42PM +0200, Johannes Berg wrote:
> > This one doesn't break 32-bit build by simply disabling irqtrace for
> > 32-bit.
>
> This looks really cool to me. I'd love to see this in 2.6.23.
Hmm. Looks like I just found a case where I forgot to trace in some
place:
[ 241.629380] hardirqs last enabled at (1319): [<c0000000000b3718>] .get_page_from_freelist+0x298/0x620
[ 241.629714] hardirqs last disabled at (1320): [<c000000000033690>] .native_hpte_invalidate+0x60/0x320
[ 241.629732] softirqs last enabled at (1282): [<c000000000059e28>] .__do_softirq+0x198/0x1e0
[ 241.629748] softirqs last disabled at (1273): [<c00000000000c7e4>] .do_softirq+0xd4/0xe0
I'll look into it when I get around.
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 190 bytes --]
^ permalink raw reply
* [PATCH 2/2] eHEA: Receive SKB Aggregation
From: Jan-Bernd Themann @ 2007-07-05 7:26 UTC (permalink / raw)
To: Jeff Garzik
Cc: Thomas Klein, Jan-Bernd Themann, netdev, linux-kernel, linux-ppc,
Christoph Raisch, Marcus Eder, Stefan Roscher
This patch enables the receive side processing to aggregate TCP packets within
the HEA device driver. It analyses the packets already received after an
interrupt arrived and forwards these as chains of SKBs for the same TCP
connection with modified header field. We have seen a lower CPU load and
improved throughput for small numbers of parallel TCP connections.
As this feature is considered as experimental it is switched off by default
and can be activated via a module parameter.
Signed-off-by: Jan-Bernd Themann <themann@de.ibm.com>
---
drivers/net/ehea/ehea.h | 30 ++++
drivers/net/ehea/ehea_main.c | 324 +++++++++++++++++++++++++++++++++++++++++-
2 files changed, 348 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ehea/ehea.h b/drivers/net/ehea/ehea.h
index f03f070..65e6c8e 100644
--- a/drivers/net/ehea/ehea.h
+++ b/drivers/net/ehea/ehea.h
@@ -55,6 +55,7 @@
#define EHEA_MAX_ENTRIES_RQ3 16383
#define EHEA_MAX_ENTRIES_SQ 32767
#define EHEA_MIN_ENTRIES_QP 127
+#define EHEA_LRO_MAX_PKTS 60
#define EHEA_SMALL_QUEUES
#define EHEA_NUM_TX_QP 1
@@ -84,6 +85,8 @@
#define EHEA_RQ2_PKT_SIZE 1522
#define EHEA_L_PKT_SIZE 256 /* low latency */
+#define MAX_LRO_DESCRIPTORS 8
+
/* Send completion signaling */
/* Protection Domain Identifier */
@@ -340,6 +343,29 @@ struct ehea_q_skb_arr {
};
/*
+ * Large Receive Offload (LRO) descriptor for a tcp session
+ */
+struct ehea_lro {
+ struct sk_buff *parent;
+ struct sk_buff *last_skb;
+ struct iphdr *iph;
+ struct tcphdr *tcph;
+
+ u32 tcp_rcv_tsecr;
+ u32 tcp_rcv_tsval;
+ u32 tcp_ack;
+ u32 tcp_next_seq;
+ u32 skb_tot_frags_len;
+ u16 ip_tot_len;
+ u16 tcp_saw_tstamp; /* timestamps enabled */
+ u16 tcp_window;
+ u16 vlan_tag;
+ int skb_sg_cnt; /* counts aggregated skbs */
+ int vlan_packet;
+ int active;
+};
+
+/*
* Port resources
*/
struct ehea_port_res {
@@ -368,6 +394,9 @@ struct ehea_port_res {
u64 tx_packets;
u64 rx_packets;
u32 poll_counter;
+ struct ehea_lro lro[MAX_LRO_DESCRIPTORS];
+ u64 lro_desc;
+ struct port_stats p_state;
};
@@ -417,6 +446,7 @@ struct ehea_port {
u32 msg_enable;
u32 sig_comp_iv;
u32 state;
+ u32 lro_max_aggr;
u8 full_duplex;
u8 autoneg;
u8 num_def_qps;
diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c
index 383144d..c283643 100644
--- a/drivers/net/ehea/ehea_main.c
+++ b/drivers/net/ehea/ehea_main.c
@@ -34,6 +34,7 @@
#include <linux/list.h>
#include <linux/if_ether.h>
#include <net/ip.h>
+#include <net/tcp.h>
#include "ehea.h"
#include "ehea_qmr.h"
@@ -52,6 +53,8 @@ static int rq2_entries = EHEA_DEF_ENTRIES_RQ2;
static int rq3_entries = EHEA_DEF_ENTRIES_RQ3;
static int sq_entries = EHEA_DEF_ENTRIES_SQ;
static int use_mcs = 0;
+static int use_lro = 0;
+static int lro_max_pkts = EHEA_LRO_MAX_PKTS;
static int num_tx_qps = EHEA_NUM_TX_QP;
module_param(msg_level, int, 0);
@@ -60,6 +63,8 @@ module_param(rq2_entries, int, 0);
module_param(rq3_entries, int, 0);
module_param(sq_entries, int, 0);
module_param(use_mcs, int, 0);
+module_param(use_lro, int, 0);
+module_param(lro_max_pkts, int, 0);
module_param(num_tx_qps, int, 0);
MODULE_PARM_DESC(num_tx_qps, "Number of TX-QPS");
@@ -77,6 +82,10 @@ MODULE_PARM_DESC(sq_entries, " Number of entries for the Send Queue "
"[2^x - 1], x = [6..14]. Default = "
__MODULE_STRING(EHEA_DEF_ENTRIES_SQ) ")");
MODULE_PARM_DESC(use_mcs, " 0:NAPI, 1:Multiple receive queues, Default = 1 ");
+MODULE_PARM_DESC(lro_max_pkts, " LRO: Max packets to be aggregated. Default = "
+ __MODULE_STRING(EHEA_LRO_MAX_PKTS));
+MODULE_PARM_DESC(use_lro, " Large Receive Offload, 1: enable, 0: disable, "
+ "Default = 0");
static int port_name_cnt = 0;
@@ -380,6 +389,297 @@ static int ehea_treat_poll_error(struct ehea_port_res *pr, int rq,
return 0;
}
+static int try_get_ip_tcp_hdr(struct ehea_cqe *cqe, struct sk_buff *skb,
+ struct iphdr **iphdr, struct tcphdr **tcph)
+{
+ unsigned int ip_hdrlength;
+ struct iphdr *iph;
+
+ /* non tcp/udp packets */
+ if (!cqe->header_length)
+ return -1;
+
+ /* non tcp packet */
+ skb_reset_network_header(skb);
+ iph = ip_hdr(skb);
+ if (iph->protocol != IPPROTO_TCP)
+ return -1;
+
+ ip_hdrlength = ip_hdrlen(skb);
+
+ /* check ip header: packet length */
+ if (iph->tot_len > cqe->num_bytes_transfered - ETH_HLEN)
+ return -1;
+
+ /* check ip header: minimal ip header received */
+ if (ip_hdrlength > iph->tot_len - 20)
+ return -1;
+
+ skb_set_transport_header(skb, ip_hdrlength);
+ *tcph = tcp_hdr(skb);
+
+ /* check if ip header and tcp header are complete */
+ if (iph->tot_len < ip_hdrlength + tcp_hdrlen(skb))
+ return -1;
+
+ *iphdr = iph;
+ return 0;
+}
+
+#define TCP_PAYLOAD_LENGTH(iph, tcph) \
+(ntohs(iph->tot_len) - (iph->ihl << 2) - (tcph->doff << 2))
+
+#define IPH_LEN_WO_OPTIONS 5
+#define TCPH_LEN_WO_OPTIONS 5
+#define TCPH_LEN_W_TIMESTAMP 8
+
+static int lro_tcp_check(struct iphdr *iph, struct tcphdr *tcph,
+ int tcp_data_len, struct ehea_lro *lro)
+{
+ if (tcp_data_len == 0)
+ return -1;
+
+ if (iph->ihl != IPH_LEN_WO_OPTIONS)
+ return -1;
+
+ if (tcph->cwr || tcph->ece || tcph->urg || !tcph->ack || tcph->psh
+ || tcph->rst || tcph->syn || tcph->fin)
+ return -1;
+
+ if (INET_ECN_is_ce(ipv4_get_dsfield(iph)))
+ return -1;
+
+ if (tcph->doff != TCPH_LEN_WO_OPTIONS
+ && tcph->doff != TCPH_LEN_W_TIMESTAMP)
+ return -1;
+
+ /* check tcp options (only timestamp allowed) */
+ if (tcph->doff == TCPH_LEN_W_TIMESTAMP) {
+ u32 *topt = (u32 *)(tcph + 1);
+
+ if (*topt != htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16)
+ | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP))
+ return -1;
+
+ /* timestamp should be in right order */
+ topt++;
+ if (lro && (ntohl(lro->tcp_rcv_tsval) > ntohl(*topt)))
+ return -1;
+
+ /* timestamp reply should not be zero */
+ topt++;
+ if (*topt == 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static void update_tcp_ip_header(struct ehea_lro *lro)
+{
+ struct iphdr *iph = lro->iph;
+ struct tcphdr *tcph = lro->tcph;
+ u32 *p;
+
+ tcph->ack_seq = lro->tcp_ack;
+ tcph->window = lro->tcp_window;
+
+ if (lro->tcp_saw_tstamp) {
+ p = (u32 *)(tcph + 1);
+ *(p+2) = lro->tcp_rcv_tsecr;
+ }
+
+ iph->tot_len = htons(lro->ip_tot_len);
+ iph->check = 0;
+ iph->check = ip_fast_csum((u8 *)lro->iph, iph->ihl);
+}
+
+static void init_lro_desc(struct ehea_lro *lro, struct ehea_cqe *cqe,
+ struct sk_buff *skb, struct iphdr *iph,
+ struct tcphdr *tcph, u32 tcp_data_len)
+{
+ u32 *ptr;
+
+ lro->parent = skb;
+ lro->iph = iph;
+ lro->tcph = tcph;
+ lro->tcp_next_seq = ntohl(tcph->seq) + tcp_data_len;
+ lro->tcp_ack = ntohl(tcph->ack_seq);
+
+ lro->skb_sg_cnt = 1;
+ lro->ip_tot_len = ntohs(iph->tot_len);
+
+ if (tcph->doff == 8) {
+ ptr = (u32 *)(tcph+1);
+ lro->tcp_saw_tstamp = 1;
+ lro->tcp_rcv_tsval = *(ptr+1);
+ lro->tcp_rcv_tsecr = *(ptr+2);
+ }
+
+ if (cqe->status & EHEA_CQE_VLAN_TAG_XTRACT) {
+ lro->vlan_packet = 1;
+ lro->vlan_tag = cqe->vlan_tag;
+ }
+
+ lro->active = 1;
+}
+
+static inline void clear_lro_desc(struct ehea_lro *lro)
+{
+ memset(lro, 0, sizeof(struct ehea_lro));
+}
+
+static void lro_add_packet(struct ehea_lro *lro, struct sk_buff *skb,
+ struct tcphdr *tcph, u32 tcp_len)
+{
+ struct sk_buff *parent = lro->parent;
+ u32 *topt;
+
+ lro->skb_sg_cnt++;
+
+ lro->ip_tot_len += tcp_len;
+ lro->tcp_next_seq += tcp_len;
+ lro->tcp_window = lro->tcph->window;
+ lro->tcp_ack = lro->tcph->ack_seq;
+
+ if (lro->tcp_saw_tstamp) {
+ topt = (u32 *) (tcph + 1);
+ lro->tcp_rcv_tsval = *(topt + 1);
+ lro->tcp_rcv_tsecr = *(topt + 2);
+ }
+
+ parent->len += tcp_len;
+ parent->data_len += tcp_len;
+
+ skb_pull(skb, (skb->len - tcp_len));
+ parent->truesize += skb->truesize;
+
+ if (lro->last_skb)
+ lro->last_skb->next = skb;
+ else
+ skb_shinfo(parent)->frag_list = skb;
+
+ lro->last_skb = skb;
+
+ return;
+}
+
+static int check_tcp_conn(struct ehea_lro *lro, struct iphdr *iph,
+ struct tcphdr *tcph)
+{
+ if ((lro->iph->saddr != iph->saddr) || (lro->iph->daddr != iph->daddr) ||
+ (lro->tcph->source != tcph->source) || (lro->tcph->dest != tcph->dest))
+ return -1;
+ return 0;
+}
+
+static void flush_lro(struct ehea_port_res *pr, struct ehea_lro *lro)
+{
+ update_tcp_ip_header(lro);
+
+ if (lro->vlan_packet && pr->port->vgrp)
+ vlan_hwaccel_receive_skb(lro->parent, pr->port->vgrp,
+ lro->vlan_tag);
+ else
+ netif_receive_skb(lro->parent);
+
+ clear_lro_desc(lro);
+}
+
+static void flush_all_lro(struct ehea_port_res *pr)
+{
+ int i;
+ struct ehea_lro *lro;
+
+ for (i = 0; i < MAX_LRO_DESCRIPTORS; i++) {
+ lro = &pr->lro[i];
+ if (lro->active)
+ flush_lro(pr, lro);
+ }
+}
+
+static struct ehea_lro *ehea_get_lro(struct ehea_port_res *pr,
+ struct iphdr *iph, struct tcphdr *tcph)
+{
+ struct ehea_lro *lro = NULL;
+ struct ehea_lro *tmp;
+ int i;
+
+ for (i = 0; i < MAX_LRO_DESCRIPTORS; i++) {
+ tmp = &pr->lro[i];
+ if (tmp->active)
+ if (!check_tcp_conn(tmp, iph, tcph)) {
+ lro = tmp;
+ goto out;
+ }
+ }
+
+ for (i = 0; i < MAX_LRO_DESCRIPTORS; i++) {
+ if(!pr->lro[i].active) {
+ lro = &pr->lro[i];
+ goto out;
+ }
+ }
+
+out:
+ return lro;
+}
+
+static void ehea_proc_skb(struct ehea_port_res *pr, struct ehea_cqe *cqe,
+ struct sk_buff *skb)
+{
+ struct iphdr *iph;
+ struct tcphdr *tcph;
+ struct ehea_lro *lro;
+ int tcp_data_len;
+ int skip_orig_skb = 0;
+
+ if (use_lro) {
+ if (try_get_ip_tcp_hdr(cqe, skb, &iph, &tcph))
+ goto out;
+
+ lro = ehea_get_lro(pr, iph, tcph);
+ if (!lro)
+ goto out;
+
+ tcp_data_len = TCP_PAYLOAD_LENGTH(iph, tcph);
+
+ if (!lro->active) {
+ if (lro_tcp_check(iph, tcph, tcp_data_len, NULL))
+ goto out;
+
+ init_lro_desc(lro, cqe, skb, iph, tcph, tcp_data_len);
+ return;
+ }
+
+ if (lro->tcp_next_seq != ntohl(tcph->seq)) {
+ flush_lro(pr, lro);
+ goto out;
+ }
+
+ if (lro_tcp_check(iph, tcph, tcp_data_len, lro)) {
+ flush_lro(pr, lro);
+ goto out;
+ }
+
+ lro_add_packet(lro, skb, tcph, tcp_data_len);
+
+ if (lro->skb_sg_cnt > pr->port->lro_max_aggr)
+ flush_lro(pr, lro);
+
+ skip_orig_skb = 1;
+ }
+
+out:
+ if (skip_orig_skb)
+ return;
+
+ if ((cqe->status & EHEA_CQE_VLAN_TAG_XTRACT) && pr->port->vgrp)
+ vlan_hwaccel_receive_skb(skb, pr->port->vgrp, cqe->vlan_tag);
+ else
+ netif_receive_skb(skb);
+}
+
static struct ehea_cqe *ehea_proc_rwqes(struct net_device *dev,
struct ehea_port_res *pr,
int *budget)
@@ -426,6 +726,7 @@ static struct ehea_cqe *ehea_proc_rwqes(struct net_device *dev,
if (!skb)
break;
}
+ skb_reserve(skb, NET_IP_ALIGN);
skb_copy_to_linear_data(skb, ((char*)cqe) + 64,
cqe->num_bytes_transfered - 4);
ehea_fill_skb(port->netdev, skb, cqe);
@@ -451,12 +752,7 @@ static struct ehea_cqe *ehea_proc_rwqes(struct net_device *dev,
processed_rq3++;
}
- if ((cqe->status & EHEA_CQE_VLAN_TAG_XTRACT)
- && port->vgrp)
- vlan_hwaccel_receive_skb(skb, port->vgrp,
- cqe->vlan_tag);
- else
- netif_receive_skb(skb);
+ ehea_proc_skb(pr, cqe, skb);
} else {
pr->p_stats.poll_receive_errors++;
port_reset = ehea_treat_poll_error(pr, rq, cqe,
@@ -468,6 +764,9 @@ static struct ehea_cqe *ehea_proc_rwqes(struct net_device *dev,
cqe = ehea_poll_rq1(qp, &wqe_index);
}
+ if (use_lro)
+ flush_all_lro(pr);
+
pr->rx_packets += processed;
*budget -= processed;
@@ -1684,9 +1983,15 @@ out:
static int ehea_change_mtu(struct net_device *dev, int new_mtu)
{
+ struct ehea_port *port = netdev_priv(dev);
+
if ((new_mtu < 68) || (new_mtu > EHEA_MAX_PACKET_SIZE))
return -EINVAL;
dev->mtu = new_mtu;
+
+ if (use_lro)
+ port->lro_max_aggr = (0xFFFF / new_mtu);
+
return 0;
}
@@ -2491,6 +2796,7 @@ struct ehea_port *ehea_setup_single_port(struct ehea_adapter *adapter,
struct ehea_port *port;
struct device *port_dev;
int jumbo;
+ int lro_pkts;
/* allocate memory for the port structures */
dev = alloc_etherdev(sizeof(struct ehea_port));
@@ -2565,6 +2871,12 @@ struct ehea_port *ehea_setup_single_port(struct ehea_adapter *adapter,
goto out_unreg_port;
}
+ lro_pkts = (0xFFFF / dev->mtu);
+ if (lro_pkts < lro_max_pkts)
+ port->lro_max_aggr = lro_pkts;
+ else
+ port->lro_max_aggr = lro_max_pkts;
+
ret = ehea_get_jumboframe_status(port, &jumbo);
if (ret)
ehea_error("failed determining jumbo frame status for %s",
--
1.5.2
^ permalink raw reply related
* [PATCH 1/2] eHEA: Capability flag for DLPAR support
From: Jan-Bernd Themann @ 2007-07-05 7:26 UTC (permalink / raw)
To: Jeff Garzik
Cc: Thomas Klein, Jan-Bernd Themann, netdev, linux-kernel, linux-ppc,
Christoph Raisch, Marcus Eder, Stefan Roscher
This patch introduces a capability flag that is used by the DLPAR userspace
tool to check which DLPAR features are supported by the eHEA driver.
Missing goto has been included.
Signed-off-by: Jan-Bernd Themann <themann@de.ibm.com>
---
drivers/net/ehea/ehea.h | 8 +++++++-
drivers/net/ehea/ehea_main.c | 23 ++++++++++++++++++++++-
2 files changed, 29 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ehea/ehea.h b/drivers/net/ehea/ehea.h
index abaf3ac..f03f070 100644
--- a/drivers/net/ehea/ehea.h
+++ b/drivers/net/ehea/ehea.h
@@ -39,7 +39,13 @@
#include <asm/io.h>
#define DRV_NAME "ehea"
-#define DRV_VERSION "EHEA_0065"
+#define DRV_VERSION "EHEA_0067"
+
+/* EHEA capability flags */
+#define DLPAR_PORT_ADD_REM 1
+#define DLPAR_MEM_ADD 2
+#define DLPAR_MEM_REM 4
+#define EHEA_CAPABILITIES (DLPAR_PORT_ADD_REM)
#define EHEA_MSG_DEFAULT (NETIF_MSG_LINK | NETIF_MSG_TIMER \
| NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR)
diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c
index bdb5241..383144d 100644
--- a/drivers/net/ehea/ehea_main.c
+++ b/drivers/net/ehea/ehea_main.c
@@ -2923,6 +2923,15 @@ static int check_module_parm(void)
return ret;
}
+static ssize_t ehea_show_capabilities(struct device_driver *drv,
+ char *buf)
+{
+ return sprintf(buf, "%d", EHEA_CAPABILITIES);
+}
+
+static DRIVER_ATTR(capabilities, S_IRUSR | S_IRGRP | S_IROTH,
+ ehea_show_capabilities, NULL);
+
int __init ehea_module_init(void)
{
int ret;
@@ -2934,8 +2943,19 @@ int __init ehea_module_init(void)
if (ret)
goto out;
ret = ibmebus_register_driver(&ehea_driver);
- if (ret)
+ if (ret) {
ehea_error("failed registering eHEA device driver on ebus");
+ goto out;
+ }
+
+ ret = driver_create_file(&ehea_driver.driver,
+ &driver_attr_capabilities);
+ if (ret) {
+ ehea_error("failed to register capabilities attribute, ret=%d",
+ ret);
+ ibmebus_unregister_driver(&ehea_driver);
+ goto out;
+ }
out:
return ret;
@@ -2943,6 +2963,7 @@ out:
static void __exit ehea_module_exit(void)
{
+ driver_remove_file(&ehea_driver.driver, &driver_attr_capabilities);
ibmebus_unregister_driver(&ehea_driver);
}
--
1.5.2
^ permalink raw reply related
* Re: TQM5200 problems (kernel 2.4)
From: Domen Puncer @ 2007-07-05 6:54 UTC (permalink / raw)
To: Kimmo Surakka; +Cc: linuxppc-embedded
In-Reply-To: <a44e83440707040751o4f2e195dte8c2edafd5f864c@mail.gmail.com>
On 04/07/07 17:51 +0300, Kimmo Surakka wrote:
> Hello all,
>
Hi!
...
> Problem #2: I2C unreliability. My test setup has two slaves connected
> on the TQM5200 I2C bus #1. On default setup this bus is disabled, so I
> needed to edit file drivers/i2c/i2c-tqm5200.c and changethe value of
> MPC5xxx_I2C1_ENABLE. After this change, the bus gets initialised.
> However, it's not reliable. I send smbus_read_word and
> smbus_write_word commands to the slaves every few milliseconds. Sooner
> or later the bus gets stuck, apparently forever. I found an old I2c
This is probably a hardware bug as described in:
http://ozlabs.org/pipermail/linuxppc-embedded/2005-July/019038.html
> patch for mpc5200 and modified it a bit. With the patch applied, the
> kernel tries to detect bus lock-ups and reset the bus. This helps a
> lot: now the lock-ups are only temporary. However, the dmesg log gets
> polluted with lines like
>
> Warning: kfree_skb on hard IRQ c008ecfc
> Warning: kfree_skb on hard IRQ c008ecfc
> Warning: kfree_skb on hard IRQ c008ecfc
> Warning: kfree_skb on hard IRQ c008ecfc
> Warning: kfree_skb on hard IRQ c008ecfc
> Warning: kfree_skb on hard IRQ c008ecfc
>
> (always the same IRQ). The seem to come from net/core/skbuff.c, and
> caused by kfree_skb being called while in_irq().
>
I'm not seeing these (2.6-git from kernel.org)
Domen
^ permalink raw reply
* Re: [PATCH] pseries: don't die if unknown/missing interrupt controller property
From: Sonny Rao @ 2007-07-05 2:35 UTC (permalink / raw)
To: linuxppc-dev, paulus; +Cc: miltonm
In-Reply-To: <20070702004937.GA15712@kevlar.boston.burdell.org>
On Sun, Jul 01, 2007 at 08:49:37PM -0400, Sonny Rao wrote:
> The pseries platform does not have a default function for init_IRQ and
> does not install one if it doesn't find or doesn't recognize an
> interrupt controller in the device tree. Currently, the kernel dies
> when it tries to call the NULL init_IRQ() function. Clean that up.
>
> Signed-off-by: Sonny Rao <sonny@burdell.org>
>
> --- kernel/arch/powerpc/platforms/pseries/setup.c~orig 2007-06-26 16:23:38.000000000 -0500
> +++ kernel/arch/powerpc/platforms/pseries/setup.c 2007-06-26 19:09:53.000000000 -0500
> @@ -274,6 +274,12 @@ static void pseries_lpar_enable_pmcs(voi
> get_lppaca()->pmcregs_in_use = 1;
> }
>
> +static void no_irq_init(void)
> +{
> + printk(KERN_ERR "no_irq_init: failed to recognize"
> + " interrupt-controller\n");
> +}
> +
> static void __init pseries_discover_pic(void)
> {
> struct device_node *np;
> @@ -296,8 +302,6 @@ static void __init pseries_discover_pic(
> return;
> }
> }
> - printk(KERN_ERR "pSeries_discover_pic: failed to recognize"
> - " interrupt-controller\n");
> }
>
> static void __init pSeries_setup_arch(void)
> @@ -594,4 +598,5 @@ define_machine(pseries) {
> .check_legacy_ioport = pSeries_check_legacy_ioport,
> .system_reset_exception = pSeries_system_reset_exception,
> .machine_check_exception = pSeries_machine_check_exception,
> + .init_IRQ = no_irq_init,
> };
Hi Paul, if there's no objection I'd like this to go into 2.6.23 thanks.
^ permalink raw reply
* A uart driver to control modem
From: Andrea Rosetti @ 2007-07-03 8:31 UTC (permalink / raw)
To: linuxppc-embedded
Hi everybody,
I'm a newbe on linux emb development.
In a mpc880 based board (kernel 2.6.19) I've to control a modem
using a scc with handshake signals on general I/O ports.
Reading code of cpm_uart driver, it seems don't manage
handshake signals whereas uart driver (<2.6.10) seems do this.
It's correct?
Could you suggest the right way to start?
Thank you,
Andrea
--
H.S.E. Systems
Smartcard & Loyalty Solutions
G. Di Vincenzo, 2
67100 - L'Aquila - Italy
Tel. 08621965154
Mobile 3406874961
Fax 0862319822
^ permalink raw reply
* Please pull powerpc.git merge branch
From: Paul Mackerras @ 2007-07-05 1:45 UTC (permalink / raw)
To: torvalds; +Cc: linuxppc-dev
Linus,
Did my pull request fall through the cracks, or is there a problem
with it?
Please do
git pull \
git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc.git merge
Thanks,
Paul.
arch/powerpc/configs/mpc7448_hpc2_defconfig | 212 +++++++-----------
arch/powerpc/configs/mpc8272_ads_defconfig | 293 +++++++++++++++----------
arch/powerpc/configs/mpc8313_rdb_defconfig | 310 +++++++++++++-------------
arch/powerpc/configs/mpc832x_mds_defconfig | 176 ++++++---------
arch/powerpc/configs/mpc832x_rdb_defconfig | 229 ++++++++-----------
arch/powerpc/configs/mpc834x_itx_defconfig | 265 +++++++++++-----------
arch/powerpc/configs/mpc834x_itxgp_defconfig | 232 ++++++++++---------
arch/powerpc/configs/mpc834x_mds_defconfig | 195 ++++++++--------
arch/powerpc/configs/mpc836x_mds_defconfig | 176 ++++++---------
arch/powerpc/configs/mpc8540_ads_defconfig | 201 ++++++++---------
arch/powerpc/configs/mpc8544_ds_defconfig | 193 +++++++---------
arch/powerpc/configs/mpc8560_ads_defconfig | 201 ++++++++---------
arch/powerpc/configs/mpc8568mds_defconfig | 191 +++++++---------
arch/powerpc/configs/mpc85xx_cds_defconfig | 206 +++++++++--------
arch/powerpc/configs/mpc8641_hpcn_defconfig | 200 ++++++++---------
arch/powerpc/configs/mpc866_ads_defconfig | 213 +++++++++---------
arch/powerpc/configs/mpc885_ads_defconfig | 222 +++++++++----------
arch/powerpc/kernel/irq.c | 6 +
arch/powerpc/platforms/cell/cbe_cpufreq.c | 15 +
drivers/net/Kconfig | 2
include/asm-powerpc/irq.h | 5
21 files changed, 1781 insertions(+), 1962 deletions(-)
commit 795bb15e0735bd2b0015ca333fa157399b72c168
Author: Kumar Gala <galak@kernel.crashing.org>
Date: Mon Jul 2 00:04:36 2007 -0500
[POWERPC] Update defconfigs
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
commit 9f7905812e5919862acb6ede63e10d3d1bb4958e
Author: Olof Johansson <olof@lixom.net>
Date: Mon Jun 4 14:47:04 2007 +1000
[POWERPC] Uninline and export virq_to_hw() for the pasemi_mac driver
Uninline virq_to_hw and export it so modules can use it. The alternative
would be to export the irq_map array instead, but it's an infrequently
called function, and keeping the array unexported seems considerably
cleaner.
This is needed so that the pasemi_mac driver can be compiled as a module.
Signed-off-by: Olof Johansson <olof@lixom.net>
Signed-off-by: Paul Mackerras <paulus@samba.org>
commit ee5d1b7f2a01ce4d95ca247b5a499b72f31cdbe8
Author: Christian Krafft <krafft@de.ibm.com>
Date: Thu Jun 28 21:14:38 2007 +1000
[POWERPC] Fix PMI breakage in cbe_cbufreq driver
The recent change to cell_defconfig to enable cpufreq on Cell exposed
the fact that the cbe_cpufreq driver currently needs the PMI interface
code to compile, but Kconfig doesn't make sure that the PMI interface
code gets built if cbe_cpufreq is enabled.
In fact cbe_cpufreq can work without PMI, so this ifdefs out the code
that deals with PMI. This is a minimal solution for 2.6.22; a more
comprehensive solution will be merged for 2.6.23.
Signed-off-by: Christian Krafft <krafft@de.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
commit 86affd5a00a92e491e5f00ed659492767519fdf7
Author: David Gibson <david@gibson.dropbear.id.au>
Date: Thu Jun 28 11:27:57 2007 +1000
[POWERPC] Disable old EMAC driver in arch/powerpc
The EMAC driver, in drivers/net/ibm_emac, for the embedded Ethernet
MAC found in PowerPC 4xx embedded chips is not suitable for
arch/powerpc. It will not build because it relies on the old arch/ppc
OCP mechanism. BenH has a new, device-tree aware version of the
driver which will work in arch/powerpc, but until it's merged, this
patch will disable the old, non-building version.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Paul Mackerras <paulus@samba.org>
^ permalink raw reply
* Re: [PATCH] powerpc: Add of_register_i2c_devices()
From: Guennadi Liakhovetski @ 2007-07-04 17:50 UTC (permalink / raw)
To: Segher Boessenkool; +Cc: linuxppc-dev, Paul Mackerras, i2c
In-Reply-To: <030A74A7-7D15-46C3-9ACE-21A42D395E65@kernel.crashing.org>
On Wed, 4 Jul 2007, Segher Boessenkool wrote:
> > be filled by respective drivers themselves.
>
> That would be nicer yes, but a bigger change.
Aha, but nicer!:-)
> > For this to work i2c would need something similar to what pci, usb do -
> > register supported device ids. The only difference is that instead of
> > numerical IDs we have to use plain text names for i2c devices...
>
> Yeah, that would be nice. Are you suggesting the Linux I2C
> layer should use "OF-style" names as its "native" naming
> scheme? I'd rather keep the namespaces separate, coupling
> them always seems like a great idea and always turns out a
> disaster.
Sorry, I'm probably missing something here. What's the principal
difference between the "OF-style" and "native" names? Wouldn't "rs5c372b"
be a natural name for both the DT "compatible" property and the list of
driver supported devices in i2c?
The only thing that I can see here is devices with not so obvious names.
OTOH, we do want all .dts to use the same name for the same chip, soooo...
> > Yes, this is why I put "would". Looks like this is the common powerpc
> > practice ATM - to make such a glue to map arbitrary "OF names" to what
> > respective drivers react to. Like in the case of the serial driver.
>
> Yes. This is the most flexible scheme possible and allows
> for all kinds of fixups/workarounds in case of broken device
> trees (or broken kernel code, for that matter).
Well, I think, I might bite. But - mostly for the above argument, i.e.,
just because it offers slightly more flexibility, doesn't require changes
to existing drivers / subsystems...
> > But -
> > i2c is much more diverse and dynamic than serial, so, maybe it is worth
> > thinking about "fixing" i2c?
>
> Be my guest :-) I care more about the OF side of things, but
> let me ask anyway -- what do you see as "broken" in the Linux
> IIC "core" that needs fixing here?
No, I put "fixing" in quotes, i.e., I didn't really mean it was some
brokenness. Just nobody needed this until now. Just that device - driver
matching in i2c happens based on driver id, not device id, like in pci,
usb, maybe others.
Thanks
Guennadi
---
Guennadi Liakhovetski
^ permalink raw reply
* Re: [PATCH] ucc_geth.c, make PHY device optional.
From: Joakim Tjernlund @ 2007-07-04 16:44 UTC (permalink / raw)
To: Segher Boessenkool; +Cc: linuxppc-dev Development, Netdev
In-Reply-To: <B27F0350-E5ED-48BF-9605-1E6BE29B9E36@kernel.crashing.org>
On Wed, 2007-07-04 at 15:32 +0200, Segher Boessenkool wrote:
> > How about separate autoneg to a property "dumb-phy", which
> > indicates the
> > PHY/switch doesn't provide MII register interface.
>
> Something like that I suppose. But don't call it "dumb phy",
> nor "fake phy", nor anything similar -- there simply is _no_
> phy. If the Linux code wants to pretend there is one, that's
> one thing, but there is no need to do any of this trickery in
> the device tree.
>
> > Therefore, it should
> > use the fixed speed and duplex from device node rather than registers.
>
> Yes.
Not sure I follow, something like this?
initial-phy-capacity = <speed duplex>
no-mdio-interface
autoneg does not fit in there.
Jocke
^ permalink raw reply
* RE: ML403 gigabit ethernet bandwidth - 2.6 kernel
From: Mohammad Sadegh Sadri @ 2007-07-04 15:17 UTC (permalink / raw)
To: eemingliu, akonovalov, linuxppc-embedded
DQpEZWFyIEFsbCwNCg0KT3VyIHRlc3RzIHNob3cgdGhhdCBDUFUgdXNhZ2UgaXMgYWx3YXlzIDEw
MCUgZHVyaW5nIG5ldHBlcmYgdGVzdC4NClNvIHRoZSBzcGVlZCBvZiBDUFUgaXMgaW1wb3J0YW50
IGluIHRoZSBvdmVyYWxsIHBlcmZvcm1hbmNlIG9mIHRoZSBnaWdhYml0IGxpbmsuDQpJZiB3ZSBj
YW4gaW5jcmVhc2UgdGhlIENQVSBjb3JlIGNsb2NrIGZyZXF1ZW5jeSB3ZSBtYXkgYWNoaWV2ZSBi
ZXR0ZXIgcmVzdWx0cyB1c2luZyBleGlzdGluZyBoYXJkd2FyZS9zb2Z0d2FyZSBjb25maWd1cmF0
aW9uLg0KDQpJIGtub3cgdGhhdCB0aGUgUFBDIGNvcmUgaW5zaWRlIEZYMTIgY2FuIHJ1biB3aXRo
IGNsb2NrIGZyZXF1ZW5jaWVzIHVwIHRvIDQ1ME1IeiwgSG93ZXZlciBCYXNlIFN5c3RlbSBCdWls
ZGVyIGZvciBNTDQwMyBhbGxvd3MganVzdCBmcmVxdWVuY2llcyBvZiB1cCB0byAzMDBNSHogZm9y
IFBQQyBjb3JlLiBEb2VzIGFueSBib2R5IGhlcmUga25vdyBob3cgSSBjYW4gbWFrZSBQUEMgY29y
ZSB0byBydW4gd2l0aCA0MDBNSHogaW4gTUw0MDM/DQoNCnRoYW5rcw0KDQoNCg0KDQotLS0tLS0t
LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQo+IEZyb206IGVlbWluZ2xpdUBob3Rt
YWlsLmNvbQ0KPiBUbzogbWFtc2FkZWdoQGhvdG1haWwuY29tDQo+IENDOiBha29ub3ZhbG92QHJ1
Lm12aXN0YS5jb207IGxpbnV4cHBjLWVtYmVkZGVkQG96bGFicy5vcmcNCj4gU3ViamVjdDogUkU6
IE1MNDAzIGdpZ2FiaXQgZXRoZXJuZXQgYmFuZHdpZHRoIC0gMi42IGtlcm5lbA0KPiBEYXRlOiBU
dWUsIDI2IEp1biAyMDA3IDE4OjEyOjU1ICswMDAwDQo+IA0KPiBEZWFyIE1vaGFtbWFkLA0KPiAN
Cj4gPk1MNDAzLS0tPlBDIDogNDEwTWJpdHMvcw0KPiA+UEMtLS0+TUw0MDMgOiAyMTBNYml0cy9z
DQo+IA0KPiBUaGVzZSByZXN1bHRzIGFyZSBpbnRlcmVzdGluZy4gSW4gcHJpY2lwbGUsIGJvYXJk
IHRvIFBDIHdpbGwgYmUgbGVzcyB0aGFuIA0KPiBQQyB0byBib2FyZC4gQW5kIGFsc28geW91IGJv
YXJkIHRvIFBDIHNwZWVkIGlzIHF1aXRlIGZhc3QuIEkgbmV2ZXIgaGFkIHRoYXQgDQo+IGhpZ2gg
YmVmb3JlLiA6KQ0KPiANCj4gPldlIGhhdmUgZGVzY3JpYmVkIHRoZSBjaGFyYWN0ZXJpc3RpY3Mg
b2Ygb3VyIGJhc2Ugc3lzdGVtIGluIHByZXZpb3VzIHBvc3RzIA0KPiBoZXJlDQo+ID4NCj4gPklu
IGFkZGl0aW9uYSB3ZSBoYXZlIDoNCj4gPjEtIGVuYWJsZWQgdGhlIHBwYyBjYWNoZXMNCj4gDQo+
IFRoaXMgd2lsbCBoZWxwIHRoZSBpbXByb3ZlbWVudCBxdWl0ZSBhIGxvdC4NCj4gDQo+ID4yLSB3
ZSBoYXZlIHNldCBCRF9JTl9CUkFNIGluIGFkYXB0ZXIuYyB0byAxLiAoIGRlZmF1bHQgaXMgMCAp
DQo+IA0KPiBBY3R1YWxseSBJIGRpZG4ndCB0cnkgdG8gbW9kaWZ5IHRoaXMgYmVmb3JlLiBNeSBw
cmV2aW91cyByZXN1bHRzIGFyZSBiYXNlZCANCj4gb24gYmRfTk9UX2luX2JyYW0uIDopIEZyb20g
bXkgdW5kZXJzdGFuZGluZywgZW5hYmxlIHRoaXMgb3B0aW9uIHdpbGwgcHV0IA0KPiB0aGUgYnVm
ZmVyIGRlc2NyaXB0b3IgaW4gQlJBTSByYXRoZXIgdGhhbiBERFIuIFBlcmhhcHMgSSBjYW4gYWxz
byB0cnkgaXQgDQo+IGFuZCB0byBzZWUgaWYgdGhlcmUgaXMgYW55IGltcHJvdmVtZW50IG9uIG15
IHN5c3RlbS4NCj4gDQo+ID5UWF9USFJFU0hPTEQgaXMgMTYgYW5kIFJYX1RIUkVTSE9MRCBpcyAy
Lg0KPiA+DQo+ID50aGUgdmlydGV4NCBmeDEyIGRldmljZSBvbiBNTDQwMyBpcyBub3cgY29tcGxl
dGVseSBmdWxsLCB3ZSBkbyBub3QgaGF2ZSANCj4gYW55IGZyZWUgYmxvY2sgbWVtb3JpZXMgbm9y
IGFueSBsb2dpYyBzbGljZXMuIE1heWJlIGlmIHdlIGhhZCBtb3JlIHNwYWNlIHdlIA0KPiBjb3Vs
ZCBjaG9vc2UgaGlnaGVyIHZhbHVlcyBmb3IgWFRFX1NFTkRfQkRfQ05UIGFuZCBYVEVfUkVDVl9C
RF9DTlQgaS5lLiANCj4gMzg0LiBEbyB5b3UgdGhpbmsgdGhpcyB3aWxsIGltcHJvdmUgcGVyZm9y
bWFuY2U/DQo+IA0KPiBQcm9iYWJseSB5ZXMuIEJ1dCBJIG5ldmVyIG1vZGlmaWVkIHRoZXNlIG51
bWJlcnMgYmVmb3JlLiBNeSBkZWZhdWx0IG9uZXMgDQo+IGFyZSA1MTIgcmVzcGVjdGl2ZWx5Lg0K
PiANCj4gPlRoZXJlIGlzIGFsc28gYW5vdGhlciBpbnRlcmVzdGluZyB0ZXN0LA0KPiA+V2UgZXhl
Y3V0ZWQgbmV0cGVyZiBvbiBib3RoIG9mIFBDIGFuZCBNTDQwMyBzaW11bHRhbm91c2x5ICwgd2hl
biB3ZSBkbyBub3QgDQo+IHB1dCBCRHMgaW4gQlJBTSwgdGhlIHBlcmZvcm1hbmNlIG9mIE1MNDAz
LS0+UEMgbGluayBkcm9wcyBmcm9tIDM5ME1iaXRzIHRvIA0KPiA0NU1iaXRzLCBidXQgd2hlbiB1
c2luZyBQTEIgQlJBTXMgZm9yIEJEcyB0aGUgcGVyZm9ybWFuY2UgZGVjcmVhc2VzIGZyb20gDQo+
IDQxME1iaXRzL3MgdG8ganVzdCAxMzBNYml0YS9zLiBJdCBpcyBpbXBvcnRhbnQgd2hlbiB0aGUg
dXNlciB3YW50cyB0byANCj4gdHJhbnNmZXIgZGF0YSBpbiBib3RoIGRpcmVjdGlvbnMgc2ltdWxh
dGFub3VzbHkuDQo+IA0KPiBEZWZpbml0ZWx5ISBUaGUgYm90dGxlbmVjayBpcyBDUFUgcHJvY2Vz
c2luZyBjYXBhYmlsaXR5LiBTbyBpZiB5b3Ugc2VuZCBhbmQgDQo+IHJlY2VpdmUgZGF0YSBhdCB0
aGUgc2FtZSB0aW1lLCB0aGUgcmVzdWx0cyB3aWxsIGJlIG11Y2ggd29yc2UuIEkgdGhpbmsgDQo+
IGFub3RoZXIgcmVhc29uIGlzIFRDUCBpcyBndWFyYW50ZWVkIHByb3RvY2FsLiBTbyB0aGVyZSB3
aWxsIGJlIHNvbWUgDQo+IGFja25vd2xlZGdlbWVudHMgcmV0dXJuaW5nIGJhY2sgd2hlbiB5b3Ug
c2VuZCBwYWNrZXRzIG91dC4gVGh1cyB5b3VyIA0KPiBiYW5kd2lkdGggd2lsbCBiZSB0YWtlbiBh
IGxpdHRsZSBhd2F5LiBIb3dldmVyIGNvbXBhcmVkIHdpdGggdGhlIA0KPiBjb25zdW1wdGlvbiBv
ZiBDUFUsIHRoaXMgcGVyaGFwcyB3aWxsIGJlIHRyaXZpYWwuDQo+IA0KPiBCUg0KPiBNaW5nDQo+
IA0KPiANCj4gDQo+IA0KPiA+DQo+ID4NCj4gPi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t
LS0tLS0tLS0tLS0NCj4gPiA+IEZyb206IGVlbWluZ2xpdUBob3RtYWlsLmNvbQ0KPiA+ID4gVG86
IG1hbXNhZGVnaEBob3RtYWlsLmNvbQ0KPiA+ID4gQ0M6IGFrb25vdmFsb3ZAcnUubXZpc3RhLmNv
bTsgbGludXhwcGMtZW1iZWRkZWRAb3psYWJzLm9yZw0KPiA+ID4gU3ViamVjdDogUkU6IE1MNDAz
IGdpZ2FiaXQgZXRoZXJuZXQgYmFuZHdpZHRoIC0gMi42IGtlcm5lbA0KPiA+ID4gRGF0ZTogTW9u
LCAyNSBKdW4gMjAwNyAxMDowMzozMCArMDAwMA0KPiA+ID4NCj4gPiA+IERlYXIgTW9oYW1tYWQs
DQo+ID4gPg0KPiA+ID4gPlRoZSByZXN1bHRzIGFyZSBhcyBmb2xsb3dzOg0KPiA+ID4gPlBDLS0+
TUw0MDMNCj4gPiA+ID5UQ1BfU0VOREZJTEUgOiAzOE1icHMNCj4gPiA+ID4NCj4gPiA+ID5NTDQw
My0tLT5QQw0KPiA+ID4gPlRDUF9TRU5ERklMRTogMTU1TWJwcw0KPiA+ID4NCj4gPiA+IFRoaXMg
cmVzdWx0IGlzIHVucmVhc29uYWJsZS4gQmVjYXVzZSBQQyBpcyBtb3JlIHBvd2VyZnVsIHRoYW4g
eW91ciANCj4gYm9hcmQsDQo+ID4gPiBzbyBQQy0+Ym9hcmQgc2hvdWxkIGJlIGZhc3RlciB0aGFu
IGJvYXJkLT5QQy4NCj4gPiA+DQo+ID4gPiA+VGhlIHRyYW5zZmVyIHJhdGUgZnJvbSBNTDQwMyB0
byBQQyBoYXMgaW1wcm92ZWQgYnkgYSBmYWN0b3Igb2YgMiwNCj4gPiA+ID5JIHNlZSBvbiB0aGUg
cG9zdHMgaGVyZSBpbiB0aGUgbWFpbGluZyBsaXN0IHRoYXQgeW91IGhhdmUgcmVhY2hlZCBhIA0K
PiBiYW5kDQo+ID4gPiB3aWR0aCBvZiAzMDFNYnBzLg0KPiA+ID4NCj4gPiA+IFllcywgd2l0aCBh
bGwgZmVhdHVyZXMgd2hpY2ggY291bGQgaW1wcm92ZSBwZXJmb3JtYW5jZSBlbmFibGVkLCB3ZSBj
YW4gDQo+IGdldA0KPiA+ID4gYXJvdW5kIDMwME1icHMgZm9yIFRDUCB0cmFuc2Zlci4gb25lIG1v
cmUgaGludCwgZGlkIHlvdSBlbmFibGUgY2FjaGVzIA0KPiBvbg0KPiA+ID4geW91ciBzeXN0ZW0/
IHBlcmhhcHMgaXQgd2lsbCBoZWxwLiBBbnl3YXksIGRvdWJsZSBjaGVjayB5b3VyIGhhcmR3YXJl
DQo+ID4gPiBkZXNpZ24gdG8gbWFrZSBzdXJlIGFsbCBmZWF0dXJlcyBhcmUgZW5hYmxlZC5UaGF0
J3MgYWxsIEkgY2FuIHN1Z2dlc3QuDQo+ID4gPg0KPiA+ID4gQlINCj4gPiA+IE1pbmcNCj4gPiA+
DQo+ID4gPg0KPiA+ID4gPg0KPiA+ID4gPg0KPiA+ID4gPg0KPiA+ID4gPg0KPiA+ID4gPi0tLS0t
LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCj4gPiA+ID4gPiBGcm9tOiBlZW1p
bmdsaXVAaG90bWFpbC5jb20NCj4gPiA+ID4gPiBUbzogbWFtc2FkZWdoQGhvdG1haWwuY29tOyBh
a29ub3ZhbG92QHJ1Lm12aXN0YS5jb207DQo+ID4gPiBsaW51eHBwYy1lbWJlZGRlZEBvemxhYnMu
b3JnOyBncmFudC5saWtlbHlAc2VjcmV0bGFiLmNhDQo+ID4gPiA+ID4gU3ViamVjdDogUkU6IE1M
NDAzIGdpZ2FiaXQgZXRoZXJuZXQgYmFuZHdpZHRoIC0gMi42IGtlcm5lbA0KPiA+ID4gPiA+IERh
dGU6IFNhdCwgMjMgSnVuIDIwMDcgMTk6MTA6MTYgKzAwMDANCj4gPiA+ID4gPg0KPiA+ID4gPiA+
IFVzZSB0aGUgZm9sbG93aW5nIGNvbW1hbmQgaW4gTGludXggcGxlYXNlOg0KPiA+ID4gPiA+DQo+
ID4gPiA+ID4gaWZjb25maWcgZXRoMCBtdHUgODk4Mg0KPiA+ID4gPiA+DQo+ID4gPiA+ID4gQXMg
d2VsbCB5b3Ugc2hvdWxkIGRvIHRoYXQgb24geW91ciBQQyBpbiB0aGUgbWVhc3VyZW1lbnQuDQo+
ID4gPiA+ID4NCj4gPiA+ID4gPiBNaW5nDQo+ID4gPiA+ID4NCj4gPiA+ID4gPg0KPiA+ID4gPiA+
ID5Gcm9tOiBNb2hhbW1hZCBTYWRlZ2ggU2FkcmkNCj4gPiA+ID4gPiA+VG86IE1pbmcgTGl1ICwN
Cj4gPiA+ID4gPiAsLA0KPiA+ID4gPiA+DQo+ID4gPiA+ID4gPlN1YmplY3Q6IFJFOiBNTDQwMyBn
aWdhYml0IGV0aGVybmV0IGJhbmR3aWR0aCAtIDIuNiBrZXJuZWwNCj4gPiA+ID4gPiA+RGF0ZTog
U2F0LCAyMyBKdW4gMjAwNyAxOTowODoyOSArMDAwMA0KPiA+ID4gPiA+ID4NCj4gPiA+ID4gPiA+
DQo+ID4gPiA+ID4gPkRlYXIgTWluZywNCj4gPiA+ID4gPiA+DQo+ID4gPiA+ID4gPlJlYWxseSB0
aGFua3MgZm9yIHJlcGx5LA0KPiA+ID4gPiA+ID4NCj4gPiA+ID4gPiA+YWJvdXQgdGhyZXNob2xk
cyBhbmQgd2FpdGJvdW5kIE9LISBJJ2xsIGFkanVzdCB0aGVtIGluIGFkYXB0ZXIuYyAsDQo+ID4g
PiA+ID4gPg0KPiA+ID4gPiA+ID5idXQgd2hhdCBhYm91dCBlbmFibGluZyBqdW1ibyBmcmFtZXM/
IHNob3VsZCBJIGRvIGFueSB0aGluZyANCj4gc3BlY2lhbCB0bw0KPiA+ID4gPiA+IGVuYWJsZSBK
dW1ibyBmcmFtIHN1cHBvcnQ/DQo+ID4gPiA+ID4gPg0KPiA+ID4gPiA+ID53ZSB3ZXJlIHRoaW5r
aW5nIHRoYXQgaXQgaXMgZW5hYmxlZCBieSBkZWZhdWx0LiBJcyBpdD8NCj4gPiA+ID4gPiA+DQo+
ID4gPiA+ID4gPnRoYW5rcw0KPiA+ID4gPiA+ID4NCj4gPiA+ID4gPiA+DQo+ID4gPiA+ID4gPg0K
PiA+ID4gPiA+ID4NCj4gPiA+ID4gPiA+LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t
LS0tLS0tLQ0KPiA+ID4gPiA+ID4gPiBGcm9tOiBlZW1pbmdsaXVAaG90bWFpbC5jb20NCj4gPiA+
ID4gPiA+ID4gVG86IG1hbXNhZGVnaEBob3RtYWlsLmNvbTsgYWtvbm92YWxvdkBydS5tdmlzdGEu
Y29tOw0KPiA+ID4gPiA+IGxpbnV4cHBjLWVtYmVkZGVkQG96bGFicy5vcmc7IGdyYW50Lmxpa2Vs
eUBzZWNyZXRsYWIuY2ENCj4gPiA+ID4gPiA+ID4gU3ViamVjdDogUkU6IE1MNDAzIGdpZ2FiaXQg
ZXRoZXJuZXQgYmFuZHdpZHRoIC0gMi42IGtlcm5lbA0KPiA+ID4gPiA+ID4gPiBEYXRlOiBTYXQs
IDIzIEp1biAyMDA3IDE4OjQ4OjE5ICswMDAwDQo+ID4gPiA+ID4gPiA+DQo+ID4gPiA+ID4gPiA+
IERlYXIgTW9oYW1tYWQsDQo+ID4gPiA+ID4gPiA+IFRoZXJlIGFyZSBzb21lIHBhcmFtZXRlcnMg
d2hpY2ggY291bGQgYmUgYWRqdXN0ZWQgdG8gaW1wcm92ZSANCj4gdGhlDQo+ID4gPiA+ID4gPiA+
IHBlcmZvcm1hbmNlLiBUaGV5IGFyZTogVFggYW5kIFJYX1RocmVzaG9sZCBUWCBhbmQgUlhfd2Fp
dGJvdW5kLiANCj4gSW4NCj4gPiA+IG15DQo+ID4gPiA+ID4gPiA+IHN5c3RlbSwgd2UgdXNlIFRY
X1RocmVzaG9sZD0xNiBhbmQgUnhfVGhyZXNob2xkPTggYW5kIGJvdGgNCj4gPiA+IHdhaXRib3Vu
ZD0xLg0KPiA+ID4gPiA+ID4gPg0KPiA+ID4gPiA+ID4gPiBBbHNvIEp1bWJvIGZyYW1lIG9mIDg5
ODIgY291bGQgYmUgZW5hYmxlLg0KPiA+ID4gPiA+ID4gPg0KPiA+ID4gPiA+ID4gPiBUcnkgdGhv
c2UgaGludHMgYW5kIHNoYXJlIHlvdXIgaW1wcm92ZW1lbnQgd2l0aCB1cy4NCj4gPiA+ID4gPiA+
ID4NCj4gPiA+ID4gPiA+ID4gQlINCj4gPiA+ID4gPiA+ID4gTWluZw0KPiA+ID4gPiA+ID4gPg0K
PiA+ID4gPiA+ID4gPiA+RnJvbTogTW9oYW1tYWQgU2FkZWdoIFNhZHJpDQo+ID4gPiA+ID4gPiA+
ID5UbzogQW5kcmVpIEtvbm92YWxvdiAsIExpbnV4IFBQQyBMaW51eA0KPiA+ID4gPiA+ID4gPiBQ
UEMsIEdyYW50IExpa2VseQ0KPiA+ID4gPiA+ID4gPiA+U3ViamVjdDogTUw0MDMgZ2lnYWJpdCBl
dGhlcm5ldCBiYW5kd2lkdGggLSAyLjYga2VybmVsDQo+ID4gPiA+ID4gPiA+ID5EYXRlOiBTYXQs
IDIzIEp1biAyMDA3IDEyOjE5OjEyICswMDAwDQo+ID4gPiA+ID4gPiA+ID4NCj4gPiA+ID4gPiA+
ID4gPg0KPiA+ID4gPiA+ID4gPiA+RGVhciBhbGwsDQo+ID4gPiA+ID4gPiA+ID4NCj4gPiA+ID4g
PiA+ID4gPlJlY2VudGx5IHdlIGRpZCBhIHNldCBvZiB0ZXN0cyBvbiBwZXJmb3JtYW5jZSBvZiB2
aXJ0ZXggNEZYIA0KPiBoYXJkDQo+ID4gPiBURU1BQw0KPiA+ID4gPiA+ID4gPiBtb2R1bGUgdXNp
bmcgTUw0MDMNCj4gPiA+ID4gPiA+ID4gPg0KPiA+ID4gPiA+ID4gPiA+d2Ugc3R1ZGllZCBhbGwg
b2YgdGhlIHBvc3RzIGhlcmUgY2FyZWZ1bGx5OiB0aGVzZSBhcmUgdGhlIA0KPiBzeXN0ZW0NCj4g
PiA+ID4gPiA+ID4gY2hhcmFjdGVyaXN0aWNzOw0KPiA+ID4gPiA+ID4gPiA+DQo+ID4gPiA+ID4g
PiA+ID5Cb2FyZCA6IE1MNDAzDQo+ID4gPiA+ID4gPiA+ID5FREsgICAgOiBFREs5LjFTUDINCj4g
PiA+ID4gPiA+ID4gPkhhcmQgVEVNQUMgdmVyc2lvbiBhbmQgUExURU1BQyB2ZXJzaW9uIGFyZSBi
b3RoIDMuMC5hDQo+ID4gPiA+ID4gPiA+ID5QUEMgY2xvY2sgZnJlcXVlbmN5IDogIDMwME1Ieg0K
PiA+ID4gPiA+ID4gPiA+S2VybmVsIDogMi42LjIxLXJjNyAsIGRvd25sb2FkZWQgZnJvbSBncmFu
dCdzIGdpdCB0cmVlIHNvbWUgDQo+IHRoaW5nDQo+ID4gPiBuZWFyDQo+ID4gPiA+ID4gb25lDQo+
ID4gPiA+ID4gPiA+IHdlZWsgYWdvDQo+ID4gPiA+ID4gPiA+ID5ETUEgdHlwZTogMyAoc2cgZG1h
KQ0KPiA+ID4gPiA+ID4gPiA+RFJFIDogZW5hYmxlZCBmb3IgVFggYW5kIFJYLCAoMikNCj4gPiA+
ID4gPiA+ID4gPkNTVU0gb2ZmbG9hZCBpcyBlbmFibGVkIGZvciBib3RoIG9mIFRYIGFuZCBSWA0K
PiA+ID4gPiA+ID4gPiA+dHggYW5kIHJ4IGZpZm8gc2l6ZXMgOiAxMzEwNzIgYml0cw0KPiA+ID4g
PiA+ID4gPiA+DQo+ID4gPiA+ID4gPiA+ID50aGUgYm9hcmQgY29tZXMgdXAgb3ZlciBORlMgcm9v
dCBmaWxlIHN5c3RlbSBjb21wbGV0ZWx5IGFuZA0KPiA+ID4gd2l0aG91dA0KPiA+ID4gPiA+IGFu
eQ0KPiA+ID4gPiA+ID4gPiBwcm9ibGVtcy4NCj4gPiA+ID4gPiA+ID4gPg0KPiA+ID4gPiA+ID4g
PiA+UEMgc3lzdGVtIHVzZWQgZm9yIHRoZXNlIHRlc3RzIGlzIDogQ1BVIFA0IER1YWwgQ29yZSwg
My40R0h6ICwNCj4gPiA+ID4gPiAyR2lnYWJ5dGVzDQo+ID4gPiA+ID4gPiA+IG1lbW9yeSwgRHVh
bCBnaWdhYml0IGV0aGVybmV0IHBvcnQsIHJ1bm5pbmcgbGludXggMi42LjIxLjMNCj4gPiA+ID4g
PiA+ID4gPldlIGhhdmUgdGVzdGVkIHRoZSBQQyBzeXN0ZW0gYmFuZCB3aWR0aCBhbmQgaXQgY2Fu
IGVhc2lseSANCj4gcmVhY2gNCj4gPiA+ID4gPiA5NjZtYml0cy9zDQo+ID4gPiA+ID4gPiA+IHdo
ZW4gY29ubmVjdGVkIHRvIHRoZSBzYW1lIFBDLiAoIHVzaW5nIHRoZSBzYW1lIGNyb3NzIGNhYmxl
IA0KPiB1c2VkDQo+ID4gPiBmb3INCj4gPiA+ID4gPiBtbDQwMw0KPiA+ID4gPiA+ID4gPiB0ZXN0
KQ0KPiA+ID4gPiA+ID4gPiA+DQo+ID4gPiA+ID4gPiA+ID5OZXRwZXJmIGlzIGNvbXBpbGVkIHdp
dGggVENQIFNFTkQgRklMRSBlbmFibGVkLCAoIA0KPiAtREhBVkVfU0VOREZJTEUpDQo+ID4gPiA+
ID4gPiA+ID4NCj4gPiA+ID4gPiA+ID4gPihmcm9tIGJvYXJkIHRvIFBDKQ0KPiA+ID4gPiA+ID4g
PiA+bmV0cGVyZiAtdCBUQ1BfU0VOREZJTEUgLUggMTAuMTAuMTAuMjUwIC1GIC9ib290L3pJbWFn
ZS5lbGYgLS0gDQo+IC1tDQo+ID4gPiA+ID4gMTYzODQgLXMNCj4gPiA+ID4gPiA+ID4gODczODAg
LVMgODczODANCj4gPiA+ID4gPiA+ID4gPg0KPiA+ID4gPiA+ID4gPiA+dGhlIG1lYXN1cmVkIGJh
bmR3aWR0aCBmb3IgdGhpcyB0ZXN0IHdhcyBqdXN0IDQwLjY2TWJpdHMuDQo+ID4gPiA+ID4gPiA+
ID5JdCBpcyBhbHNvIHRydWUgZm9yIG5ldHBlcmYgZnJvbSBQQyB0byBib2FyZC4NCj4gPiA+ID4g
PiA+ID4gPg0KPiA+ID4gPiA+ID4gPiA+d2UgZG8gbm90IGhhdmUgYW55IG1vcmUgaWRlYSBhYm91
dCB3aGF0IHdlIHNob3VsZCBkbyB0byANCj4gaW1wcm92ZQ0KPiA+ID4gdGhlDQo+ID4gPiA+ID4g
PiA+IGJhbmR3aWR0aC4NCj4gPiA+ID4gPiA+ID4gPmFueSBoZWxwIG9yIGlkZWFzIGlzIGFwcHJl
Y2lhdGVkLi4uDQo+ID4gPiA+ID4gPiA+ID4NCj4gPiA+ID4gPiA+ID4gDQo+ID5fX19fX19fX19f
X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fXw0K
PiA+ID4gPiA+ID4gPiA+Q29ubmVjdCB0byB0aGUgbmV4dCBnZW5lcmF0aW9uIG9mIE1TTg0KPiA+
ID4gPiA+ID4gPg0KPiA+ID4gPiA+DQo+ID4gPiANCj4gTWVzc2VuZ2VyPz5odHRwOi8vaW1hZ2lu
ZS1tc24uY29tL21lc3Nlbmdlci9sYXVuY2g4MC9kZWZhdWx0LmFzcHg/bG9jYWxlPWVuLXVzJnNv
dXJjZT13bG1haWx0YWdsaW5lDQo+IA0KPiA+ID4NCj4gPiA+ID4gPg0KPiA+ID4gPiA+ID4gPg0K
PiA+ID4gPiA+ID4gPiA+X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f
X19fX18NCj4gPiA+ID4gPiA+ID4gPkxpbnV4cHBjLWVtYmVkZGVkIG1haWxpbmcgbGlzdA0KPiA+
ID4gPiA+ID4gPiA+TGludXhwcGMtZW1iZWRkZWRAb3psYWJzLm9yZw0KPiA+ID4gPiA+ID4gPiA+
aHR0cHM6Ly9vemxhYnMub3JnL21haWxtYW4vbGlzdGluZm8vbGludXhwcGMtZW1iZWRkZWQNCj4g
PiA+ID4gPiA+ID4NCj4gPiA+ID4gPiA+ID4gDQo+IF9fX19fX19fX19fX19fX19fX19fX19fX19f
X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fDQo+ID4gPiA+ID4gPiA+IOWF
jei0ueS4i+i9vSBNU04gRXhwbG9yZXI6ICAgaHR0cDovL2V4cGxvcmVyLm1zbi5jb20vbGNjbi8N
Cj4gPiA+ID4gPiA+ID4NCj4gPiA+ID4gPiA+DQo+ID4gPiA+ID4gPl9fX19fX19fX19fX19fX19f
X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fDQo+ID4gPiA+
ID4gPk5ld3MsIGVudGVydGFpbm1lbnQgYW5kIGV2ZXJ5dGhpbmcgeW91IGNhcmUgYWJvdXQgYXQg
TGl2ZS5jb20uIEdldCANCj4gaXQNCj4gPiA+IG5vdyENCj4gPiA+ID4gPiA+aHR0cDovL3d3dy5s
aXZlLmNvbS9nZXRzdGFydGVkLmFzcHgNCj4gPiA+ID4gPg0KPiA+ID4gPiA+IF9fX19fX19fX19f
X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fDQo+
ID4gPiA+ID4g5YWN6LS55LiL6L29IE1TTiBFeHBsb3JlcjogICBodHRwOi8vZXhwbG9yZXIubXNu
LmNvbS9sY2NuLw0KPiA+ID4gPiA+DQo+ID4gPiA+DQo+ID4gPiA+X19fX19fX19fX19fX19fX19f
X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18NCj4gPiA+ID5E
aXNjb3ZlciB0aGUgbmV3IFdpbmRvd3MgVmlzdGENCj4gPiA+ID5odHRwOi8vc2VhcmNoLm1zbi5j
b20vcmVzdWx0cy5hc3B4P3E9d2luZG93cyt2aXN0YSZta3Q9ZW4tVVMmZm9ybT1RQlJFDQo+ID4g
Pg0KPiA+ID4gX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f
X19fX19fX19fX19fX19fX18NCj4gPiA+IOWFjei0ueS4i+i9vSBNU04gRXhwbG9yZXI6ICAgaHR0
cDovL2V4cGxvcmVyLm1zbi5jb20vbGNjbi8NCj4gPiA+DQo+ID4NCj4gPl9fX19fX19fX19fX19f
X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fDQo+ID5O
ZXdzLCBlbnRlcnRhaW5tZW50IGFuZCBldmVyeXRoaW5nIHlvdSBjYXJlIGFib3V0IGF0IExpdmUu
Y29tLiBHZXQgaXQgbm93IQ0KPiA+aHR0cDovL3d3dy5saXZlLmNvbS9nZXRzdGFydGVkLmFzcHgN
Cj4gDQo+IF9fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f
X19fX19fX19fX19fX19fDQo+IOS4juiBlOacuueahOaci+WPi+i/m+ihjOS6pOa1ge+8jOivt+S9
v+eUqCBNU04gTWVzc2VuZ2VyOiAgaHR0cDovL21lc3Nlbmdlci5tc24uY29tL2NuICANCj4gDQoN
Cl9fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f
X19fX19fX19fDQpDb25uZWN0IHRvIHRoZSBuZXh0IGdlbmVyYXRpb24gb2YgTVNOIE1lc3Nlbmdl
csKgDQpodHRwOi8vaW1hZ2luZS1tc24uY29tL21lc3Nlbmdlci9sYXVuY2g4MC9kZWZhdWx0LmFz
cHg/bG9jYWxlPWVuLXVzJnNvdXJjZT13bG1haWx0YWdsaW5l
^ permalink raw reply
* TQM5200 problems (kernel 2.4)
From: Kimmo Surakka @ 2007-07-04 14:51 UTC (permalink / raw)
To: linuxppc-embedded
[-- Attachment #1: Type: text/plain, Size: 4560 bytes --]
Hello all,
I've been playing with a TQM5200 card and trying to make its I2C
communications even relatively reliable. To communicate with the card,
I use PPP over RS-232 (on port PSC0), baud rate 115200. All the files
are stored on the card's own 32 MB flash.
On my tests, I've met three problems: PPP connection unreliability,
I2C unreliability, and flash unreliability. I anybody has any ideas on
what could be causing these, I'd be happy to hear. Also, I've put
together two patches that help a bit, so if somebody else is
struggling with the same problems, feel free to test these patches.
Problem #1: PPP unreliability. If I just fetch the latest 2.4 kernel
from http://www.denx.de/cgi-bin/gitweb.cgi?p=linuxppc_2_4_devel.git;a=summary
and build it with a pretty minimal configuration file, PPP connection
is really unreliable. I've set the MTS to be 576 bytes, because on a
continuous transfer from TQM I get CRC error about once every ten
seconds. This triggers TCP CRC recalculation code from git commits
e3145f7943db03e3808e493555b28e7696e8f408
and 8bab0e983d5fc1ad53c34c0cc68a07d9339a7148. As a result, my dmesg
log gets filled with lines like:
tcp_recheck_csum: seq 0x1e9a515 retransmit, csum 0x4bb2 OK?
tcp_recheck_csum: seq 0x1e9ad75 retransmit, csum 0x32fe OK?
tcp_recheck_csum: seq 0x1e9ad75 retransmit, csum 0x32fe OK?
tcp_recheck_csum: seq 0x1e9ad75 retransmit, csum 0x32fe OK?
tcp_recheck_csum: seq 0x1e9af8d retransmit, csum 0xc296 OK?
tcp_recheck_csum: seq 0x1e9af8d retransmit, csum 0xc272 OK?
All PPP transmission also stalls. Data just won't arrive anymore.
I put together a patch that undoes the TCP/IP workaround patches. When
I apply the patch to the latest git commit, the errors seem to go
away. PPP connection now works even though there are the occational
CRC errors due to unreliable serial line. My conclusion:
the TCP/IP workaround is somehow broken. I'm not sure what bug it was
meant to work around, but while doing that it caused serious problems
with PPP communication.
Problem #2: I2C unreliability. My test setup has two slaves connected
on the TQM5200 I2C bus #1. On default setup this bus is disabled, so I
needed to edit file drivers/i2c/i2c-tqm5200.c and changethe value of
MPC5xxx_I2C1_ENABLE. After this change, the bus gets initialised.
However, it's not reliable. I send smbus_read_word and
smbus_write_word commands to the slaves every few milliseconds. Sooner
or later the bus gets stuck, apparently forever. I found an old I2c
patch for mpc5200 and modified it a bit. With the patch applied, the
kernel tries to detect bus lock-ups and reset the bus. This helps a
lot: now the lock-ups are only temporary. However, the dmesg log gets
polluted with lines like
Warning: kfree_skb on hard IRQ c008ecfc
Warning: kfree_skb on hard IRQ c008ecfc
Warning: kfree_skb on hard IRQ c008ecfc
Warning: kfree_skb on hard IRQ c008ecfc
Warning: kfree_skb on hard IRQ c008ecfc
Warning: kfree_skb on hard IRQ c008ecfc
(always the same IRQ). The seem to come from net/core/skbuff.c, and
caused by kfree_skb being called while in_irq().
Problem #3: flash unreliability. After some time (weeks or so) the
system's flash filesystem (JFFS2) starts to misbehave. On every boot I
see lines like
jffs2_scan_eraseblock(): Node at 0x000bce2c {0x1985, 0x0000,
0x00000000) has invalid CRC 0x6ca60000 (calculated 0xbe76ea63)
jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x000bce34:
0x6ca6 instead
jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x000bce48:
0x0019 instead
jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x000bce4c:
0x42c6 instead
jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x000bce50:
0x42c6 instead
jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x000bce54:
0x42c6 instead
jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x000bce58:
0x0019 instead
jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x000bce64:
0x0600 instead
After a number of boots the system finally fails to boot at all. The
errors go away by erasing the flash and recreating the filesystem, but
the come back again at some point. Actually, I'm not sure if this
error still exists on the latest kernel sources. Before this, I've
been using the sources from snapshot
ftp://ftp.denx.de/pub/linux/linuxppc_2_4_devel-2005-10-25-1440.tar.bz2,
but since the code starts to be pretty old, I switched to using the
git repository. I'll continue tests with the latest code and see if
the problem still exists.
--
Kimmo Surakka <kusti@iki.fi>
http://www.iki.fi/kusti
[-- Attachment #2: undo-tcpip-workaround.patch --]
[-- Type: text/x-patch, Size: 8149 bytes --]
This patch undoes the git commits e3145f7943db03e3808e493555b28e7696e8f408
and 8bab0e983d5fc1ad53c34c0cc68a07d9339a7148,
"Work-around for TCP/IP errors"
diff --git a/arch/ppc/lib/Makefile b/arch/ppc/lib/Makefile
index f198de4..4951e67 100644
--- a/arch/ppc/lib/Makefile
+++ b/arch/ppc/lib/Makefile
@@ -8,7 +8,7 @@ O_TARGET := lib.o
export-objs := dec_and_lock.o
-obj-y := checksum.o string.o strcase.o dec_and_lock.o div64.o checksum_alt.o
+obj-y := checksum.o string.o strcase.o dec_and_lock.o div64.o
obj-$(CONFIG_SMP) += locks.o
diff --git a/arch/ppc/lib/checksum_alt.c b/arch/ppc/lib/checksum_alt.c
deleted file mode 100644
index 820cb28..0000000
--- a/arch/ppc/lib/checksum_alt.c
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * INET An implementation of the TCP/IP protocol suite for the LINUX
- * operating system. INET is implemented using the BSD Socket
- * interface as the means of communication with the user level.
- *
- * IP/TCP/UDP checksumming routines
- *
- * Authors: Jorge Cwik, <jorge@laser.satlink.net>
- * Arnt Gulbrandsen, <agulbra@nvg.unit.no>
- * Tom May, <ftom@netcom.com>
- * Andreas Schwab, <schwab@issan.informatik.uni-dortmund.de>
- * Lots of code moved from tcp.c and ip.c; see those files
- * for more names.
- *
- * 03/02/96 Jes Sorensen, Andreas Schwab, Roman Hodek:
- * Fixed some nasty bugs, causing some horrible crashes.
- * A: At some points, the sum (%0) was used as
- * length-counter instead of the length counter
- * (%1). Thanks to Roman Hodek for pointing this out.
- * B: GCC seems to mess up if one uses too many
- * data-registers to hold input values and one tries to
- * specify d0 and d1 as scratch registers. Letting gcc choose these
- * registers itself solves the problem.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-/* Revised by Kenneth Albanowski for m68knommu. Basic problem: unaligned access kills, so most
- of the assembly has to go. */
-
-static inline unsigned short from32to16(unsigned long x)
-{
- /* add up 16-bit and 16-bit for 16+c bit */
- x = (x & 0xffff) + (x >> 16);
- /* add up carry.. */
- x = (x & 0xffff) + (x >> 16);
- return x;
-}
-
-static unsigned long do_csum(const unsigned char * buff, int len)
-{
- int odd, count;
- unsigned long result = 0;
-
- if (len <= 0)
- goto out;
- odd = 1 & (unsigned long) buff;
- if (odd) {
- result = *buff;
- len--;
- buff++;
- }
- count = len >> 1; /* nr of 16-bit words.. */
- if (count) {
- if (2 & (unsigned long) buff) {
- result += *(unsigned short *) buff;
- count--;
- len -= 2;
- buff += 2;
- }
- count >>= 1; /* nr of 32-bit words.. */
- if (count) {
- unsigned long carry = 0;
- do {
- unsigned long w = *(unsigned long *) buff;
- count--;
- buff += 4;
- result += carry;
- result += w;
- carry = (w > result);
- } while (count);
- result += carry;
- result = (result & 0xffff) + (result >> 16);
- }
- if (len & 2) {
- result += *(unsigned short *) buff;
- buff += 2;
- }
- }
- if (len & 1)
- result += (*buff << 8);
- result = from32to16(result);
- if (odd)
- result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
-out:
- return result;
-}
-
-/*
- * computes the checksum of a memory block at buff, length len,
- * and adds in "sum" (32-bit)
- *
- * returns a 32-bit number suitable for feeding into itself
- * or csum_tcpudp_magic
- *
- * this function must be called with even lengths, except
- * for the last fragment, which may be odd
- *
- * it's best to have buff aligned on a 32-bit boundary
- */
-unsigned int csum_partial_alt(const unsigned char *buff, int len, unsigned int sum)
-{
- unsigned int result = do_csum(buff, len);
-
- /* add in old sum, and carry.. */
- result += sum;
- if(sum > result)
- result += 1;
- return result;
-}
diff --git a/include/net/tcp.h b/include/net/tcp.h
index de9dad9..b584b85 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -24,8 +24,6 @@
/* Cancel timers, when they are not required. */
#undef TCP_CLEAR_TIMERS
-#define CONFIG_TCP_CS_RECHECK 1
-
#include <linux/config.h>
#include <linux/tcp.h>
#include <linux/slab.h>
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 2931ef0..b4cd6b7 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1129,82 +1129,6 @@ out:
sock_put(sk);
}
-#ifdef CONFIG_TCP_CS_RECHECK /* Re-check the TCP header checksum */
-static u32 csum_tcpudp_nofold_alt(u32 saddr,
- u32 daddr,
- unsigned short len,
- unsigned short proto,
- unsigned int sum)
-{
- unsigned long long s = sum;
-
- s += saddr;
- s += daddr;
-
- s += (proto << 16) + ntohs(len); /*be*/
- /* s += (ntohs(len)<<16)+proto*256;*/ /*le*/
- s += (s >> 32);
-
- return (u32) s;
-}
-
-static unsigned int csum_fold_alt(unsigned int sum)
-{
- sum = (sum & 0xffff) + (sum >> 16);
- sum = (sum & 0xffff) + (sum >> 16);
- return ~sum;
-}
-
-static inline unsigned short int
-csum_tcpudp_magic_alt(unsigned long saddr, unsigned long daddr, unsigned short len,
- unsigned short proto, unsigned int sum)
-{
-
- return csum_fold_alt (csum_tcpudp_nofold_alt (saddr, daddr, len, proto, sum));
-}
-
-static u16 tcp_v4_check_alt(struct tcphdr *th, int len,
- unsigned long saddr, unsigned long daddr,
- unsigned long base)
-{
- u16 ret;
-
- ret = csum_tcpudp_magic_alt(saddr,daddr,len,IPPROTO_TCP,base);
- return ret;
-}
-
-static void tcp_recheck_csum(struct tcphdr *th, int len,
- unsigned long saddr, unsigned long daddr,
- int len2, unsigned int sum)
-{
- extern int do_recheck;
- int savedcs, altcs;
-
- if (!do_recheck)
- return;
-
- savedcs = th->check;
- th->check = 0;
- altcs = tcp_v4_check_alt(th, len, saddr, daddr,
- csum_partial_alt((char *)th, len2, sum));
- if (altcs != savedcs) {
- printk("%s: seq %d: csum mismatch: %#x, alt %#x\n", __FUNCTION__,
- ntohl(th->seq), ntohs(savedcs), ntohs(altcs));
- th->check = altcs;
- /* Check for memory corruption */
- __asm__("sync" ::: "memory");
- if (*(volatile ushort *)&(th->check) != altcs) {
- printk("%s: in mem %#x (@%#x), in reg %#x ?\n", __FUNCTION__,
- ntohs(th->check), &th->check, ntohs(altcs));
- }
- } else {
- printk("%s: seq %#x retransmit, csum %#x OK?\n", __FUNCTION__,
- ntohl(th->seq), ntohs(savedcs));
- th->check = savedcs;
- }
-}
-#endif
-
/* This routine computes an IPv4 TCP checksum. */
void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len,
struct sk_buff *skb)
@@ -1215,9 +1139,6 @@ void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len,
} else {
th->check = tcp_v4_check(th, len, sk->saddr, sk->daddr,
csum_partial((char *)th, th->doff<<2, skb->csum));
-#ifdef CONFIG_TCP_CS_RECHECK
- tcp_recheck_csum(th, len, sk->saddr, sk->daddr, th->doff<<2, skb->csum);
-#endif
}
}
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index a13b106..39c12d6 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -809,9 +809,6 @@ void tcp_simple_retransmit(struct sock *sk)
* state updates are done by the caller. Returns non-zero if an
* error occurred which prevented the send.
*/
-#ifdef CONFIG_TCP_CS_RECHECK
-int do_recheck;
-#endif
int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
{
struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
@@ -872,17 +869,9 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
*/
TCP_SKB_CB(skb)->when = tcp_time_stamp;
-#ifdef CONFIG_TCP_CS_RECHECK
- do_recheck = 1;
err = tcp_transmit_skb(sk, (skb_cloned(skb) ?
pskb_copy(skb, GFP_ATOMIC):
skb_clone(skb, GFP_ATOMIC)));
- do_recheck = 0;
-#else
- err = tcp_transmit_skb(sk, (skb_cloned(skb) ?
- pskb_copy(skb, GFP_ATOMIC):
- skb_clone(skb, GFP_ATOMIC)));
-#endif
if (err == 0) {
/* Update global TCP statistics. */
[-- Attachment #3: i2c-fix-test.patch --]
[-- Type: text/x-patch, Size: 6393 bytes --]
diff --git a/drivers/i2c/i2c-algo-mpc5xxx.c b/drivers/i2c/i2c-algo-mpc5xxx.c
index 26292c7..4130aae 100644
--- a/drivers/i2c/i2c-algo-mpc5xxx.c
+++ b/drivers/i2c/i2c-algo-mpc5xxx.c
@@ -24,13 +24,20 @@
#include <linux/i2c-algo-ppc_5xxx.h>
/* #define MPC5xxx_SCAN 1 */
-#define MPC5xxx_TIMEOUT 100
+#define MPC5xxx_TIMEOUT 1000
#define MPC5xxx_RETRIES 3
+#define MPC5xxx_SPEED_KBPS 25
-static int speed = 100;
+#define DEB(x) if (dlevel>=1) x;
+#define DEB2(x) if (dlevel>=2) x;
-MODULE_PARM(speed, "i");
+static int speed = MPC5xxx_SPEED_KBPS; /* kbps */
+static int dlevel = 1;
+MODULE_PARM(speed, "i");
+MODULE_PARM_DESC(speed, "bus clock (kbps)");
+MODULE_PARM(dlevel, "i");
+MODULE_PARM_DESC(dlevel, "debug level");
#ifdef CONFIG_MPC5xxx_I2C_INT
DECLARE_WAIT_QUEUE_HEAD(i2c1_if_wq);
@@ -48,6 +55,8 @@ static int i2c2_status = 0;
static volatile u32 *micr = (u32 *)(MPC5xxx_I2C_MICR);
#endif
+static void mpc5xxx_init(struct i2c_algo_mpc5xxx_data *algo_data);
+
static int mpc5xxx_in(volatile u32 *reg)
{
return in_be32(reg) >> 24;
@@ -75,6 +84,9 @@ static void i2c_start(struct i2c_algo_mpc5xxx_data *algo_data)
mpc5xxx_out(®s->mcr, MPC5xxx_I2C_STA, MPC5xxx_I2C_STA);
+ /* clear pending interrupts */
+ mpc5xxx_out(®s->msr, 0, MPC5xxx_I2C_IF);
+
return;
}
@@ -140,6 +152,22 @@ static void i2c2_interrupt(int irq, void *dev_id, struct pt_regs *pregs)
}
#endif
+static void i2c_reset(struct i2c_algo_mpc5xxx_data *algo_data)
+{
+ struct mpc5xxx_i2c *regs = algo_data->regs;
+
+ DEB(printk(KERN_DEBUG "i2c-algo-mpc5xxx.o: resetting bus\n"));
+
+ /* Clear status, go slave */
+ mpc5xxx_out(®s->msr, 0, 0);
+ i2c_stop(algo_data);
+
+ udelay(10000 / speed); /* 0,1 ms @ 100 kbs */
+ mpc5xxx_init(algo_data);
+
+ return;
+}
+
static int wait_for_bb(struct i2c_algo_mpc5xxx_data *algo_data)
{
struct mpc5xxx_i2c *regs = algo_data->regs;
@@ -177,7 +205,15 @@ static int wait_for_bb(struct i2c_algo_mpc5xxx_data *algo_data)
*pstat = 0;
#else
status = mpc5xxx_in(®s->msr);
+
+ if(status & MPC5xxx_I2C_AL) {
+ DEB(printk(KERN_DEBUG "i2c-algo-mpc5xxx.o: AL set in wait_for_bb, status=0x%x\n", status));
+ i2c_reset(algo_data);
+ return -1;
+ }
+
while ((time_before(jiffies,timeout)) && (status & MPC5xxx_I2C_BB)) {
+ udelay(10);
status = mpc5xxx_in(®s->msr);
}
#endif
@@ -220,13 +256,17 @@ static int wait_for_pin(struct i2c_algo_mpc5xxx_data *algo_data,
*status = mpc5xxx_in(®s->msr);
while ((time_before(jiffies,timeout)) && !(*status & MPC5xxx_I2C_IF)) {
+ udelay(10);
*status = mpc5xxx_in(®s->msr);
}
#endif
- if (!(*status & MPC5xxx_I2C_IF)) {
- return -1;
- }
+ if (!(*status & MPC5xxx_I2C_IF) || *status & MPC5xxx_I2C_AL) {
+ DEB(printk(KERN_DEBUG "i2c-algo-mpc5xxx.o: wait_for_pin: status=0x%x\n",
+ *status));
+ i2c_reset(algo_data);
+ return -1;
+ }
mpc5xxx_out(®s->msr, 0, MPC5xxx_I2C_IF);
@@ -247,7 +287,8 @@ static int mpc5xxx_get_fdr(void)
ulong ipb = CONFIG_PPC_5xxx_IPBFREQ;
#endif
ulong bestmatch = 0xffffffffUL;
- int best_i = 0, best_j = 0, i, j;
+ int best_i = 3, best_j = 7; /* values for the slowest bus */
+ int i, j;
int SCL_Tap[] = { 9, 10, 12, 15, 5, 6, 7, 8};
struct {int scl2tap, tap2tap; } scltap[] = {
{4, 1},
@@ -275,7 +316,7 @@ static int mpc5xxx_get_fdr(void)
}
}
- divider = (best_i & 3) | ((best_i & 4) << 3) | (best_j << 2);
+ divider = (best_i & 3) | ((best_i & 4) << 5) | (best_j << 2);
return divider;
}
@@ -373,12 +414,9 @@ static int mpc5xxx_sendbytes(struct i2c_adapter *adap,
for (wrcount = 0; wrcount < count; ++wrcount) {
mpc5xxx_out(®s->mdr, buf[wrcount], 0);
- if (wait_for_pin(algo_data, &status)) {
- i2c_stop(algo_data);
- return -EREMOTEIO;
- }
-
- if (status & MPC5xxx_I2C_RXAK) {
+ if (wait_for_pin(algo_data, &status) ||
+ (status & MPC5xxx_I2C_RXAK) ) {
+ DEB(printk(KERN_DEBUG "i2c-algo-mpc5xxx.o: error in mpc5xxx_sendbytes, status=0x%x\n", status));
i2c_stop(algo_data);
return -EREMOTEIO;
}
@@ -394,36 +432,33 @@ static int mpc5xxx_readbytes(struct i2c_adapter *adap,
struct i2c_algo_mpc5xxx_data *algo_data = adap->algo_data;
struct mpc5xxx_i2c *regs = algo_data->regs;
int rdcount = 0;
- int dummy = 1;
int status;
- int i;
mpc5xxx_out(®s->mcr, 0, MPC5xxx_I2C_TX);
- mpc5xxx_out(®s->mcr, 0, MPC5xxx_I2C_TXAK);
-
- for (i = 0; i < count; ++i) {
- buf[rdcount] = mpc5xxx_in(®s->mdr);
-
- if (dummy) {
- dummy = 0;
- } else {
- rdcount++;
- }
- if (i == count - 1)
- mpc5xxx_out(®s->mcr, MPC5xxx_I2C_TXAK, MPC5xxx_I2C_TXAK);
+ if(count > 1) {
+ mpc5xxx_out(®s->mcr, 0, MPC5xxx_I2C_TXAK);
+ } else {
+ mpc5xxx_out(®s->mcr, ~0, MPC5xxx_I2C_TXAK);
+ }
+ if(count > 0) {
+ mpc5xxx_in(®s->mdr);
+ }
+
+ for (rdcount = 0; rdcount < count; ++rdcount) {
if (wait_for_pin(algo_data, &status)) {
i2c_stop(algo_data);
return -EREMOTEIO;
}
- }
-
- buf[rdcount++] = mpc5xxx_in(®s->mdr);
- if (wait_for_pin(algo_data, &status)) {
- i2c_stop(algo_data);
- return -EREMOTEIO;
+ if (rdcount == count - 1) {
+ mpc5xxx_out(®s->mcr, ~0, MPC5xxx_I2C_TX);
+ }
+ else if (rdcount == count - 2) {
+ mpc5xxx_out(®s->mcr, ~0, MPC5xxx_I2C_TXAK);
+ }
+ buf[rdcount] = mpc5xxx_in(®s->mdr);
}
return rdcount;
@@ -464,18 +499,23 @@ static int mpc5xxx_xfer(struct i2c_adapter *adap,
for (i = 0; i < num; i++) {
pmsg = &msgs[i];
- if (wait_for_bb(algo_data))
+ if (wait_for_bb(algo_data)) {
+ DEB(printk(KERN_DEBUG "i2c-algo-mpc5xxx.o: wait_for_bb failed in mpc5xxx_xfer\n"));
+ i2c_reset(algo_data);
return -EREMOTEIO;
+ }
i2c_start(algo_data);
mpc5xxx_do_address(algo_data, pmsg, adap->retries);
if (wait_for_pin(algo_data, &status)) {
+ DEB(printk(KERN_DEBUG "i2c-algo-mpc5xxx.o: wait_for_pin failed in mpc5xxx_xfer, status=0x%x\n", status));
i2c_stop(algo_data);
return -EREMOTEIO;
}
- if (status & MPC5xxx_I2C_RXAK) {
+ if (status & MPC5xxx_I2C_RXAK) {
+ DEB2(printk(KERN_DEBUG "i2c-algo-mpc5xxx.o: RXAK set in mpc5xxx_xfer, status=0x%x\n", status));
i2c_stop(algo_data);
return -EREMOTEIO;
}
^ permalink raw reply related
* MPC8540 DMA transfer
From: Ansari @ 2007-07-04 14:18 UTC (permalink / raw)
To: linuxppc-embedded
[-- Attachment #1: Type: text/plain, Size: 269 bytes --]
Hello Koller,
Is there any support for dma transter in linux 2.4.x kernel . Whether u have any source code for tht ?????. I think u have attached a dma driver code for 2.6.x kernel . But i need it for 2.4 kernel. ( Processor : MPC8540 / MPC8560 )
Regards
Ansari
[-- Attachment #2: Type: text/html, Size: 811 bytes --]
^ permalink raw reply
* Re: idr_get_new_above() limitation?
From: Hoang-Nam Nguyen @ 2007-07-04 14:11 UTC (permalink / raw)
To: jim.houston
Cc: linux-kernel, openib-general, Stefan Roscher, linuxppc-dev,
raisch, Andrew Morton
In-Reply-To: <1183422700.3130.27.camel@localhost.localdomain>
On Tuesday 03 July 2007 02:31, Jim Houston wrote:
> The problem is in idr_get_new_above_int() in the loop which
> adds new layers to the top of the radix tree. It is failing
> the "layers < (MAX_LEVEL - 1)" test. It doesn't allocate the
> new layer but still calls sub_alloc() which relies on having
> the new layer properly constructed. I believe that it is
> allocating the slot which corresponds to id = 0.
Hi Jim,
Thanks for your quick reply.
Yes, I realized that while condition too and have tried with a tiny
change like (layers < MAX_LEVEL), but without success with idr_find(),
even though 6 layers were created and the object was added at proper
location. After several debug cycles I think to find the root cause
in the if-condition in idr_find():
void *idr_find(struct idr *idp, int id)
{
int n;
struct idr_layer *p;
n = idp->layers * IDR_BITS;
p = idp->top;
/* Mask off upper bits we don't use for the search. */
id &= MAX_ID_MASK;
if (id >= (1 << n))
return NULL;
...
}
Since idp->layers is now 6, n is equal 36, ie out of 32-bit-range,
and therefore
(1 << n) = (1 << 36) = 0
causing that if-cond to be true ie idr_find() fails.
Replacing that if-line by
if ((long)id >= (1L << n))
makes idr_find() working properly until MAX_ID_MASK.
Since there are other places to be changed like above as well eg.
idr_replace() and because you're creating a patch too, I'm waiting
first for your comment. Let me know if you prefer me to send a
patch.
Regards
Nam
^ permalink raw reply
* Re: [PATCH] ucc_geth.c, make PHY device optional.
From: Segher Boessenkool @ 2007-07-04 13:32 UTC (permalink / raw)
To: Li Yang-r58472; +Cc: linuxppc-dev Development, Netdev
In-Reply-To: <989B956029373F45A0B8AF0297081890D839B4@zch01exm26.fsl.freescale.net>
> How about separate autoneg to a property "dumb-phy", which
> indicates the
> PHY/switch doesn't provide MII register interface.
Something like that I suppose. But don't call it "dumb phy",
nor "fake phy", nor anything similar -- there simply is _no_
phy. If the Linux code wants to pretend there is one, that's
one thing, but there is no need to do any of this trickery in
the device tree.
> Therefore, it should
> use the fixed speed and duplex from device node rather than registers.
Yes.
Segher
^ permalink raw reply
* [patch 4/6] ps3: Disk Storage Driver
From: Geert Uytterhoeven @ 2007-07-04 13:22 UTC (permalink / raw)
To: Paul Mackerras, Jens Axboe, James E.J. Bottomley,
Alessandro Rubini
Cc: Geert Uytterhoeven, linuxppc-dev, linux-kernel, linux-scsi
In-Reply-To: <20070704132212.726923000@pademelon.sonytel.be>
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 18632 bytes --]
From: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
Add a Disk Storage Driver for the PS3:
- Implemented as a block device driver with a dynamic major
- Disk names (and partitions) are of the format ps3d%c(%u)
- Uses software scatter-gather with a 64 KiB bounce buffer as the hypervisor
doesn't support scatter-gather
CC: Geoff Levand <geoffrey.levand@am.sony.com>
Signed-off-by: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
---
v3:
- Cleanup #includes
- Kill PS3DISK_MAJOR, always use zero to obtain a dynamic major
- Group LV1 API related stuff
- Kill empty ps3disk_open
- Reset ps3disk_priv(dev) to NULL during cleanup
- Move call to put_disk() down
- Pass the interrupt handler to ps3stor_setup(), so it doesn't have to be
overridden later
- Fold ps3disk_read_write_sectors() into ps3disk_submit_request_sg()
- Identify the hard drive capacity and model name during probing
v2:
- Don't use `default y' in Kconfig
- Remove the thread for handling requests
- Set up sysfs links between block device and PS3 system device:
o /sys/block/ps3da/device -> ../../devices/ps3_system/sb_02
o /sys/devices/ps3_system/sb_02/block:ps3da -> ../../../block/ps3da
- Fix all FIXMEs
- Implement proper barrier support
- Call add_disk_randomness()
- Add a test to verify we are running on a PS3
- Fix error path in ps3disk_init()
- Fix cleanup order in ps3disk_exit()
arch/powerpc/platforms/ps3/Kconfig | 10
drivers/block/Makefile | 1
drivers/block/ps3disk.c | 623 +++++++++++++++++++++++++++++++++++++
3 files changed, 634 insertions(+)
--- a/arch/powerpc/platforms/ps3/Kconfig
+++ b/arch/powerpc/platforms/ps3/Kconfig
@@ -102,4 +102,14 @@ config PS3_STORAGE
depends on PPC_PS3
tristate
+config PS3_DISK
+ tristate "PS3 Disk Storage Driver"
+ depends on PPC_PS3 && BLOCK
+ select PS3_STORAGE
+ help
+ Include support for the PS3 Disk Storage.
+
+ This support is required to access the PS3 hard disk.
+ In general, all users will say Y or M.
+
endmenu
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -29,3 +29,4 @@ obj-$(CONFIG_VIODASD) += viodasd.o
obj-$(CONFIG_BLK_DEV_SX8) += sx8.o
obj-$(CONFIG_BLK_DEV_UB) += ub.o
+obj-$(CONFIG_PS3_DISK) += ps3disk.o
--- /dev/null
+++ b/drivers/block/ps3disk.c
@@ -0,0 +1,623 @@
+/*
+ * PS3 Disk Storage Driver
+ *
+ * Copyright (C) 2007 Sony Computer Entertainment Inc.
+ * Copyright 2007 Sony Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/ata.h>
+#include <linux/blkdev.h>
+
+#include <asm/lv1call.h>
+#include <asm/ps3stor.h>
+#include <asm/firmware.h>
+
+
+#define DEVICE_NAME "ps3disk"
+
+#define BOUNCE_SIZE (64*1024)
+
+#define PS3DISK_MAX_DISKS 16
+#define PS3DISK_MINORS 16
+
+#define KERNEL_SECTOR_SIZE 512
+
+
+#define PS3DISK_NAME "ps3d%c"
+
+
+struct ps3disk_private {
+ spinlock_t lock; /* Request queue spinlock */
+ struct request_queue *queue;
+ struct gendisk *gendisk;
+ unsigned int blocking_factor;
+ struct request *req;
+ u64 raw_capacity;
+ unsigned char model[ATA_ID_PROD_LEN+1];
+};
+#define ps3disk_priv(dev) ((dev)->sbd.core.driver_data)
+
+
+#define LV1_STORAGE_SEND_ATA_COMMAND (2)
+#define LV1_STORAGE_ATA_HDDOUT (0x23)
+
+struct lv1_ata_cmnd_block {
+ u16 features;
+ u16 sector_count;
+ u16 LBA_low;
+ u16 LBA_mid;
+ u16 LBA_high;
+ u8 device;
+ u8 command;
+ u32 is_ext;
+ u32 proto;
+ u32 in_out;
+ u32 size;
+ u64 buffer;
+ u32 arglen;
+};
+
+enum lv1_ata_proto {
+ NON_DATA_PROTO = 0,
+ PIO_DATA_IN_PROTO = 1,
+ PIO_DATA_OUT_PROTO = 2,
+ DMA_PROTO = 3
+};
+
+enum lv1_ata_in_out {
+ DIR_WRITE = 0, /* memory -> device */
+ DIR_READ = 1 /* device -> memory */
+};
+
+static int ps3disk_major;
+
+
+static struct block_device_operations ps3disk_fops = {
+ .owner = THIS_MODULE,
+};
+
+
+static void ps3disk_scatter_gather(struct ps3_storage_device *dev,
+ struct request *req, int gather)
+{
+ unsigned int sectors = 0, offset = 0;
+ struct bio *bio;
+ sector_t sector;
+ struct bio_vec *bvec;
+ unsigned int i = 0, j;
+ size_t size;
+ void *buf;
+
+ rq_for_each_bio(bio, req) {
+ sector = bio->bi_sector;
+ dev_dbg(&dev->sbd.core,
+ "%s:%u: bio %u: %u segs %u sectors from %lu\n",
+ __func__, __LINE__, i, bio_segments(bio),
+ bio_sectors(bio), sector);
+ bio_for_each_segment(bvec, bio, j) {
+ size = bio_cur_sectors(bio)*KERNEL_SECTOR_SIZE;
+ buf = __bio_kmap_atomic(bio, j, KM_USER0);
+ if (gather)
+ memcpy(dev->bounce_buf+offset, buf, size);
+ else
+ memcpy(buf, dev->bounce_buf+offset, size);
+ offset += size;
+ __bio_kunmap_atomic(bio, KM_USER0);
+ }
+ sectors += bio_sectors(bio);
+ i++;
+ }
+}
+
+static int ps3disk_submit_request_sg(struct ps3_storage_device *dev,
+ struct request *req)
+{
+ struct ps3disk_private *priv = ps3disk_priv(dev);
+ int write = rq_data_dir(req), res;
+ const char *op = write ? "write" : "read";
+ u64 start_sector, sectors;
+ unsigned int region_id = dev->regions[dev->region_idx].id;
+
+#ifdef DEBUG
+ unsigned int n = 0;
+ struct bio *bio;
+ rq_for_each_bio(bio, req)
+ n++;
+ dev_dbg(&dev->sbd.core,
+ "%s:%u: %s req has %u bios for %lu sectors %lu hard sectors\n",
+ __func__, __LINE__, op, n, req->nr_sectors,
+ req->hard_nr_sectors);
+#endif
+
+ start_sector = req->sector*priv->blocking_factor;
+ sectors = req->nr_sectors*priv->blocking_factor;
+ dev_dbg(&dev->sbd.core, "%s:%u: %s %lu sectors starting at %lu\n",
+ __func__, __LINE__, op, sectors, start_sector);
+
+ if (write) {
+ ps3disk_scatter_gather(dev, req, 1);
+
+ res = lv1_storage_write(dev->sbd.dev_id, region_id,
+ start_sector, sectors, 0,
+ dev->bounce_lpar, &dev->tag);
+ } else {
+ res = lv1_storage_read(dev->sbd.dev_id, region_id,
+ start_sector, sectors, 0,
+ dev->bounce_lpar, &dev->tag);
+ }
+ if (res) {
+ dev_err(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__,
+ __LINE__, op, res);
+ end_request(req, 0);
+ return 0;
+ }
+
+ priv->req = req;
+ return 1;
+}
+
+static int ps3disk_submit_flush_request(struct ps3_storage_device *dev,
+ struct request *req)
+{
+ struct ps3disk_private *priv = ps3disk_priv(dev);
+ u64 res;
+
+ dev_dbg(&dev->sbd.core, "%s:%u: flush request\n", __func__, __LINE__);
+
+ res = lv1_storage_send_device_command(dev->sbd.dev_id,
+ LV1_STORAGE_ATA_HDDOUT, 0, 0, 0,
+ 0, &dev->tag);
+ if (res) {
+ dev_err(&dev->sbd.core, "%s:%u: sync cache failed 0x%lx\n",
+ __func__, __LINE__, res);
+ end_request(req, 0);
+ return 0;
+ }
+
+ priv->req = req;
+ return 1;
+}
+
+static void ps3disk_do_request(struct ps3_storage_device *dev,
+ request_queue_t *q)
+{
+ struct request *req;
+
+ dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__);
+
+ while ((req = elv_next_request(q))) {
+ if (blk_fs_request(req)) {
+ if (ps3disk_submit_request_sg(dev, req))
+ break;
+ } else if (req->cmd_type == REQ_TYPE_FLUSH) {
+ if (ps3disk_submit_flush_request(dev, req))
+ break;
+ } else {
+ blk_dump_rq_flags(req, DEVICE_NAME " bad request");
+ end_request(req, 0);
+ continue;
+ }
+ }
+}
+
+static void ps3disk_request(request_queue_t *q)
+{
+ struct ps3_storage_device *dev = q->queuedata;
+ struct ps3disk_private *priv = ps3disk_priv(dev);
+
+ if (priv->req) {
+ dev_dbg(&dev->sbd.core, "%s:%u busy\n", __func__, __LINE__);
+ return;
+ }
+
+ ps3disk_do_request(dev, q);
+}
+
+static irqreturn_t ps3disk_interrupt(int irq, void *data)
+{
+ struct ps3_storage_device *dev = data;
+ struct ps3disk_private *priv;
+ struct request *req;
+ int res, read, uptodate;
+ u64 tag, status;
+ unsigned long num_sectors;
+ const char *op;
+
+ res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status);
+
+ if (tag != dev->tag)
+ dev_err(&dev->sbd.core,
+ "%s:%u: tag mismatch, got %lx, expected %lx\n",
+ __func__, __LINE__, tag, dev->tag);
+
+ if (res) {
+ dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%lx\n",
+ __func__, __LINE__, res, status);
+ return IRQ_HANDLED;
+ }
+
+ priv = ps3disk_priv(dev);
+ req = priv->req;
+ if (!req) {
+ dev_dbg(&dev->sbd.core,
+ "%s:%u non-block layer request completed\n", __func__,
+ __LINE__);
+ dev->lv1_status = status;
+ complete(&dev->done);
+ return IRQ_HANDLED;
+ }
+
+ if (req->cmd_type == REQ_TYPE_FLUSH) {
+ read = 0;
+ num_sectors = req->hard_cur_sectors;
+ op = "flush";
+ } else {
+ read = !rq_data_dir(req);
+ num_sectors = req->nr_sectors;
+ op = read ? "read" : "write";
+ }
+ if (status) {
+ dev_dbg(&dev->sbd.core, "%s:%u: %s failed 0x%lx\n", __func__,
+ __LINE__, op, status);
+ uptodate = 0;
+ } else {
+ dev_dbg(&dev->sbd.core, "%s:%u: %s completed\n", __func__,
+ __LINE__, op);
+ uptodate = 1;
+ if (read)
+ ps3disk_scatter_gather(dev, req, 0);
+ }
+
+ spin_lock(&priv->lock);
+ if (!end_that_request_first(req, uptodate, num_sectors)) {
+ add_disk_randomness(req->rq_disk);
+ blkdev_dequeue_request(req);
+ end_that_request_last(req, uptodate);
+ }
+ priv->req = NULL;
+ ps3disk_do_request(dev, priv->queue);
+ spin_unlock(&priv->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int ps3disk_sync_cache(struct ps3_storage_device *dev)
+{
+ u64 res;
+
+ dev_dbg(&dev->sbd.core, "%s:%u: sync cache\n", __func__, __LINE__);
+
+ res = ps3stor_send_command(dev, LV1_STORAGE_ATA_HDDOUT, 0, 0, 0, 0);
+ if (res) {
+ dev_err(&dev->sbd.core, "%s:%u: sync cache failed 0x%lx\n",
+ __func__, __LINE__, res);
+ return -EIO;
+ }
+ return 0;
+}
+
+
+/* ATA helpers copied from drivers/ata/libata-core.c */
+
+static void swap_buf_le16(u16 *buf, unsigned int buf_words)
+{
+#ifdef __BIG_ENDIAN
+ unsigned int i;
+
+ for (i = 0; i < buf_words; i++)
+ buf[i] = le16_to_cpu(buf[i]);
+#endif /* __BIG_ENDIAN */
+}
+
+static u64 ata_id_n_sectors(const u16 *id)
+{
+ if (ata_id_has_lba(id)) {
+ if (ata_id_has_lba48(id))
+ return ata_id_u64(id, 100);
+ else
+ return ata_id_u32(id, 60);
+ } else {
+ if (ata_id_current_chs_valid(id))
+ return ata_id_u32(id, 57);
+ else
+ return id[1] * id[3] * id[6];
+ }
+}
+
+static void ata_id_string(const u16 *id, unsigned char *s, unsigned int ofs,
+ unsigned int len)
+{
+ unsigned int c;
+
+ while (len > 0) {
+ c = id[ofs] >> 8;
+ *s = c;
+ s++;
+
+ c = id[ofs] & 0xff;
+ *s = c;
+ s++;
+
+ ofs++;
+ len -= 2;
+ }
+}
+
+static void ata_id_c_string(const u16 *id, unsigned char *s, unsigned int ofs,
+ unsigned int len)
+{
+ unsigned char *p;
+
+ WARN_ON(!(len & 1));
+
+ ata_id_string(id, s, ofs, len - 1);
+
+ p = s + strnlen(s, len - 1);
+ while (p > s && p[-1] == ' ')
+ p--;
+ *p = '\0';
+}
+
+static int ps3disk_identify(struct ps3_storage_device *dev)
+{
+ struct ps3disk_private *priv = ps3disk_priv(dev);
+ struct lv1_ata_cmnd_block ata_cmnd;
+ u16 *id = dev->bounce_buf;
+ u64 res;
+
+ dev_dbg(&dev->sbd.core, "%s:%u: identify disk\n", __func__, __LINE__);
+
+ memset(&ata_cmnd, 0, sizeof(struct lv1_ata_cmnd_block));
+ ata_cmnd.command = ATA_CMD_ID_ATA;
+ ata_cmnd.sector_count = 1;
+ ata_cmnd.size = ata_cmnd.arglen = ATA_ID_WORDS * 2;
+ ata_cmnd.buffer = dev->bounce_lpar;
+ ata_cmnd.proto = PIO_DATA_IN_PROTO;
+ ata_cmnd.in_out = DIR_READ;
+
+ res = ps3stor_send_command(dev, LV1_STORAGE_SEND_ATA_COMMAND,
+ ps3_mm_phys_to_lpar(__pa(&ata_cmnd)),
+ sizeof(ata_cmnd), ata_cmnd.buffer,
+ ata_cmnd.arglen);
+ if (res) {
+ dev_err(&dev->sbd.core, "%s:%u: identify disk failed 0x%lx\n",
+ __func__, __LINE__, res);
+ return -EIO;
+ }
+
+ swap_buf_le16(id, ATA_ID_WORDS);
+
+ /* All we're interested in are raw capacity and model name */
+ priv->raw_capacity = ata_id_n_sectors(id);
+ ata_id_c_string(id, priv->model, ATA_ID_PROD, sizeof(priv->model));
+ return 0;
+}
+
+static void ps3disk_prepare_flush(request_queue_t *q, struct request *req)
+{
+ struct ps3_storage_device *dev = q->queuedata;
+
+ dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__);
+
+ memset(req->cmd, 0, sizeof(req->cmd));
+ req->cmd_type = REQ_TYPE_FLUSH;
+}
+
+static int ps3disk_issue_flush(request_queue_t *q, struct gendisk *gendisk,
+ sector_t *sector)
+{
+ struct ps3_storage_device *dev = q->queuedata;
+ struct request *req;
+ int res;
+
+ dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__);
+
+ req = blk_get_request(q, WRITE, __GFP_WAIT);
+ ps3disk_prepare_flush(q, req);
+ res = blk_execute_rq(q, gendisk, req, 0);
+ if (res)
+ dev_err(&dev->sbd.core, "%s:%u: flush request failed %d\n",
+ __func__, __LINE__, res);
+ blk_put_request(req);
+ return res;
+}
+
+
+static unsigned long ps3disk_mask;
+
+static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
+{
+ struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+ struct ps3disk_private *priv;
+ int error;
+ unsigned int devidx;
+ struct request_queue *queue;
+ struct gendisk *gendisk;
+
+ if (dev->blk_size < KERNEL_SECTOR_SIZE) {
+ dev_err(&dev->sbd.core,
+ "%s:%u: cannot handle block size %lu\n", __func__,
+ __LINE__, dev->blk_size);
+ return -EINVAL;
+ }
+
+ BUILD_BUG_ON(PS3DISK_MAX_DISKS > BITS_PER_LONG);
+ devidx = find_first_zero_bit(&ps3disk_mask, PS3DISK_MAX_DISKS);
+ if (devidx >= PS3DISK_MAX_DISKS) {
+ dev_err(&dev->sbd.core, "%s:%u: Too many disks\n", __func__,
+ __LINE__);
+ return -ENOSPC;
+ }
+ __set_bit(devidx, &ps3disk_mask);
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ error = -ENOMEM;
+ goto fail;
+ }
+
+ ps3disk_priv(dev) = priv;
+ spin_lock_init(&priv->lock);
+
+ dev->bounce_size = BOUNCE_SIZE;
+ dev->bounce_buf = kmalloc(BOUNCE_SIZE, GFP_DMA);
+ if (!dev->bounce_buf) {
+ error = -ENOMEM;
+ goto fail_free_priv;
+ }
+
+ error = ps3stor_setup(dev, ps3disk_interrupt);
+ if (error)
+ goto fail_free_bounce;
+
+ ps3disk_identify(dev);
+
+ queue = blk_init_queue(ps3disk_request, &priv->lock);
+ if (!queue) {
+ dev_err(&dev->sbd.core, "%s:%u: blk_init_queue failed\n",
+ __func__, __LINE__);
+ error = -ENOMEM;
+ goto fail_teardown;
+ }
+
+ priv->queue = queue;
+ queue->queuedata = dev;
+
+ blk_queue_bounce_limit(queue, BLK_BOUNCE_HIGH);
+
+ blk_queue_max_sectors(queue, dev->bounce_size/KERNEL_SECTOR_SIZE);
+ blk_queue_segment_boundary(queue, -1UL);
+ blk_queue_dma_alignment(queue, dev->blk_size-1);
+ blk_queue_hardsect_size(queue, dev->blk_size);
+
+ blk_queue_issue_flush_fn(queue, ps3disk_issue_flush);
+ blk_queue_ordered(queue, QUEUE_ORDERED_DRAIN_FLUSH,
+ ps3disk_prepare_flush);
+
+ blk_queue_max_phys_segments(queue, -1);
+ blk_queue_max_hw_segments(queue, -1);
+ blk_queue_max_segment_size(queue, dev->bounce_size);
+
+ gendisk = alloc_disk(PS3DISK_MINORS);
+ if (!gendisk) {
+ dev_err(&dev->sbd.core, "%s:%u: alloc_disk failed\n", __func__,
+ __LINE__);
+ error = -ENOMEM;
+ goto fail_cleanup_queue;
+ }
+
+ priv->gendisk = gendisk;
+ gendisk->major = ps3disk_major;
+ gendisk->first_minor = devidx * PS3DISK_MINORS;
+ gendisk->fops = &ps3disk_fops;
+ gendisk->queue = queue;
+ gendisk->private_data = dev;
+ gendisk->driverfs_dev = &dev->sbd.core;
+ snprintf(gendisk->disk_name, sizeof(gendisk->disk_name), PS3DISK_NAME,
+ devidx+'a');
+ priv->blocking_factor = dev->blk_size/KERNEL_SECTOR_SIZE;
+ set_capacity(gendisk,
+ dev->regions[dev->region_idx].size*priv->blocking_factor);
+
+ dev_info(&dev->sbd.core,
+ "%s is a %s (%lu MiB total, %lu MiB for OtherOS)\n",
+ gendisk->disk_name, priv->model, priv->raw_capacity >> 11,
+ get_capacity(gendisk) >> 11);
+
+ add_disk(gendisk);
+ return 0;
+
+fail_cleanup_queue:
+ blk_cleanup_queue(queue);
+fail_teardown:
+ ps3stor_teardown(dev);
+fail_free_bounce:
+ kfree(dev->bounce_buf);
+fail_free_priv:
+ kfree(priv);
+ ps3disk_priv(dev) = NULL;
+fail:
+ __clear_bit(devidx, &ps3disk_mask);
+ return error;
+}
+
+static int ps3disk_remove(struct ps3_system_bus_device *_dev)
+{
+ struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+ struct ps3disk_private *priv = ps3disk_priv(dev);
+
+ __clear_bit(priv->gendisk->first_minor / PS3DISK_MINORS,
+ &ps3disk_mask);
+ del_gendisk(priv->gendisk);
+ blk_cleanup_queue(priv->queue);
+ put_disk(priv->gendisk);
+ dev_notice(&dev->sbd.core, "Synchronizing disk cache\n");
+ ps3disk_sync_cache(dev);
+ ps3stor_teardown(dev);
+ kfree(dev->bounce_buf);
+ kfree(priv);
+ ps3disk_priv(dev) = NULL;
+ return 0;
+}
+
+static struct ps3_system_bus_driver ps3disk = {
+ .match_id = PS3_MATCH_ID_STOR_DISK,
+ .core.name = DEVICE_NAME,
+ .core.owner = THIS_MODULE,
+ .probe = ps3disk_probe,
+ .remove = ps3disk_remove,
+ .shutdown = ps3disk_remove,
+};
+
+
+static int __init ps3disk_init(void)
+{
+ int error;
+
+ if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
+ return -ENODEV;
+
+ error = register_blkdev(0, DEVICE_NAME);
+ if (error <= 0) {
+ printk(KERN_ERR "%s:%u: register_blkdev failed %d\n", __func__,
+ __LINE__, error);
+ return error;
+ }
+ ps3disk_major = error;
+
+ pr_info("%s:%u: registered block device major %d\n", __func__,
+ __LINE__, ps3disk_major);
+
+ error = ps3_system_bus_driver_register(&ps3disk);
+ if (error)
+ unregister_blkdev(ps3disk_major, DEVICE_NAME);
+
+ return error;
+}
+
+static void __exit ps3disk_exit(void)
+{
+ ps3_system_bus_driver_unregister(&ps3disk);
+ unregister_blkdev(ps3disk_major, DEVICE_NAME);
+}
+
+module_init(ps3disk_init);
+module_exit(ps3disk_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PS3 Disk Storage Driver");
+MODULE_AUTHOR("Sony Corporation");
+MODULE_ALIAS(PS3_MODULE_ALIAS_STOR_DISK);
--
With kind regards,
Geert Uytterhoeven
Software Architect
Sony Network and Software Technology Center Europe
The Corporate Village · Da Vincilaan 7-D1 · B-1935 Zaventem · Belgium
Phone: +32 (0)2 700 8453
Fax: +32 (0)2 700 8622
E-mail: Geert.Uytterhoeven@sonycom.com
Internet: http://www.sony-europe.com/
Sony Network and Software Technology Center Europe
A division of Sony Service Centre (Europe) N.V.
Registered office: Technologielaan 7 · B-1840 Londerzeel · Belgium
VAT BE 0413.825.160 · RPR Brussels
Fortis Bank Zaventem · Swift GEBABEBB08A · IBAN BE39001382358619
^ permalink raw reply
* [patch 3/6] ps3: Storage device registration routines.
From: Geert Uytterhoeven @ 2007-07-04 13:22 UTC (permalink / raw)
To: Paul Mackerras, Jens Axboe, James E.J. Bottomley,
Alessandro Rubini
Cc: Geert Uytterhoeven, linuxppc-dev, linux-kernel, linux-scsi
In-Reply-To: <20070704132212.726923000@pademelon.sonytel.be>
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 9356 bytes --]
Add support for storage devices to the device probe code.
Signed-off-by: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>
---
v2:
- use msleep() instead of schedule_timeout()
arch/powerpc/platforms/ps3/device-init.c | 286 +++++++++++++++++++++++++++++++
1 files changed, 286 insertions(+)
--- a/arch/powerpc/platforms/ps3/device-init.c
+++ b/arch/powerpc/platforms/ps3/device-init.c
@@ -26,6 +26,7 @@
#include <asm/firmware.h>
#include <asm/lv1call.h>
+#include <asm/ps3stor.h>
#include "platform.h"
@@ -237,6 +238,262 @@ static int __init ps3_setup_vuart_device
return result;
}
+static int ps3stor_wait_for_completion(u64 dev_id, u64 tag,
+ unsigned int timeout)
+{
+ int result = -1;
+ unsigned int retries = 0;
+ u64 status;
+
+ for (retries = 0; retries < timeout; retries++) {
+ result = lv1_storage_check_async_status(dev_id, tag, &status);
+ if (!result)
+ break;
+
+ msleep(1);
+ }
+
+ if (result)
+ pr_debug("%s:%u: check_async_status: %s, status %lx\n",
+ __func__, __LINE__, ps3_result(result), status);
+
+ return result;
+}
+
+/**
+ * ps3_storage_wait_for_device - Wait for a storage device to become ready.
+ * @repo: The repository device to wait for.
+ *
+ * Uses the hypervisor's storage device notification mechanism to wait until
+ * a storage device is ready. The device notification mechanism uses a
+ * psuedo device (id = -1) to asynchronously notify the guest when storage
+ * devices become ready. The notification device has a block size of 512
+ * bytes.
+ */
+
+static int ps3_storage_wait_for_device(const struct ps3_repository_device *repo)
+{
+ int result;
+ const u64 notification_dev_id = (u64)-1LL;
+ const unsigned int timeout = HZ;
+ u64 lpar;
+ u64 tag;
+ struct {
+ u64 operation_code; /* must be zero */
+ u64 event_mask; /* 1 = device ready */
+ } *notify_cmd;
+ struct {
+ u64 event_type; /* notify_device_ready */
+ u64 bus_id;
+ u64 dev_id;
+ u64 dev_type;
+ u64 dev_port;
+ } *notify_event;
+ enum {
+ notify_device_ready = 1
+ };
+
+ pr_debug(" -> %s:%u: bus_id %u, dev_id %u, dev_type %u\n", __func__,
+ __LINE__, repo->bus_id, repo->dev_id, repo->dev_type);
+
+ notify_cmd = kzalloc(512, GFP_KERNEL);
+ notify_event = (void *)notify_cmd;
+ if (!notify_cmd)
+ return -ENOMEM;
+
+ lpar = ps3_mm_phys_to_lpar(__pa(notify_cmd));
+
+ result = lv1_open_device(repo->bus_id, notification_dev_id, 0);
+ if (result) {
+ printk(KERN_ERR "%s:%u: lv1_open_device %s\n", __func__,
+ __LINE__, ps3_result(result));
+ result = -ENODEV;
+ goto fail_free;
+ }
+
+ /* Setup and write the request for device notification. */
+
+ notify_cmd->operation_code = 0; /* must be zero */
+ notify_cmd->event_mask = 0x01; /* device ready */
+
+ result = lv1_storage_write(notification_dev_id, 0, 0, 1, 0, lpar,
+ &tag);
+ if (result) {
+ printk(KERN_ERR "%s:%u: write failed %s\n", __func__, __LINE__,
+ ps3_result(result));
+ result = -ENODEV;
+ goto fail_close;
+ }
+
+ /* Wait for the write completion */
+
+ result = ps3stor_wait_for_completion(notification_dev_id, tag,
+ timeout);
+ if (result) {
+ printk(KERN_ERR "%s:%u: write not completed %s\n", __func__,
+ __LINE__, ps3_result(result));
+ result = -ENODEV;
+ goto fail_close;
+ }
+
+ /* Loop here processing the requested notification events. */
+
+ result = -ENODEV;
+ while (1) {
+ memset(notify_event, 0, sizeof(*notify_event));
+
+ result = lv1_storage_read(notification_dev_id, 0, 0, 1, 0,
+ lpar, &tag);
+ if (result) {
+ printk(KERN_ERR "%s:%u: write failed %s\n", __func__,
+ __LINE__, ps3_result(result));
+ break;
+ }
+
+ result = ps3stor_wait_for_completion(notification_dev_id, tag,
+ timeout);
+ if (result) {
+ printk(KERN_ERR "%s:%u: read not completed %s\n",
+ __func__, __LINE__, ps3_result(result));
+ break;
+ }
+
+ if (notify_event->event_type != notify_device_ready ||
+ notify_event->bus_id != repo->bus_id) {
+ pr_debug("%s:%u: bad notify_event: event %lu, "
+ "dev_id %lu, dev_type %lu\n",
+ __func__, __LINE__, notify_event->event_type,
+ notify_event->dev_id, notify_event->dev_type);
+ break;
+ }
+
+ if (notify_event->dev_id == repo->dev_id &&
+ notify_event->dev_type == repo->dev_type) {
+ pr_debug("%s:%u: device ready: dev_id %u\n", __func__,
+ __LINE__, repo->dev_id);
+ result = 0;
+ break;
+ }
+
+ if (notify_event->dev_id == repo->dev_id &&
+ notify_event->dev_type == PS3_DEV_TYPE_NOACCESS) {
+ pr_debug("%s:%u: no access: dev_id %u\n", __func__,
+ __LINE__, repo->dev_id);
+ break;
+ }
+ }
+
+fail_close:
+ lv1_close_device(repo->bus_id, notification_dev_id);
+fail_free:
+ kfree(notify_cmd);
+ pr_debug(" <- %s:%u\n", __func__, __LINE__);
+ return result;
+}
+
+static int ps3_setup_storage_dev(const struct ps3_repository_device *repo,
+ enum ps3_match_id match_id)
+{
+ int result;
+ struct ps3_storage_device *p;
+ u64 port, blk_size, num_blocks;
+ unsigned int num_regions, i;
+
+ pr_debug(" -> %s:%u: match_id %u\n", __func__, __LINE__, match_id);
+
+ result = ps3_repository_read_stor_dev_info(repo->bus_index,
+ repo->dev_index, &port,
+ &blk_size, &num_blocks,
+ &num_regions);
+ if (result) {
+ printk(KERN_ERR "%s:%u: _read_stor_dev_info failed %d\n",
+ __func__, __LINE__, result);
+ return -ENODEV;
+ }
+
+ pr_debug("%s:%u: index %u:%u: port %lu blk_size %lu num_blocks %lu "
+ "num_regions %u\n", __func__, __LINE__, repo->bus_index,
+ repo->dev_index, port, blk_size, num_blocks, num_regions);
+
+ p = kzalloc(sizeof(struct ps3_storage_device) +
+ num_regions * sizeof(struct ps3_storage_region),
+ GFP_KERNEL);
+ if (!p) {
+ result = -ENOMEM;
+ goto fail_malloc;
+ }
+
+ p->sbd.match_id = match_id;
+ p->sbd.dev_type = PS3_DEVICE_TYPE_SB;
+ p->sbd.bus_id = repo->bus_id;
+ p->sbd.dev_id = repo->dev_id;
+ p->sbd.d_region = &p->dma_region;
+ p->blk_size = blk_size;
+ p->num_regions = num_regions;
+
+ result = ps3_repository_find_interrupt(repo,
+ PS3_INTERRUPT_TYPE_EVENT_PORT,
+ &p->sbd.interrupt_id);
+ if (result) {
+ printk(KERN_ERR "%s:%u: find_interrupt failed %d\n", __func__,
+ __LINE__, result);
+ result = -ENODEV;
+ goto fail_find_interrupt;
+ }
+
+ /* FIXME: Arrange to only do this on a 'cold' boot */
+
+ result = ps3_storage_wait_for_device(repo);
+ if (result) {
+ printk(KERN_ERR "%s:%u: storage_notification failed %d\n",
+ __func__, __LINE__, result);
+ result = -ENODEV;
+ goto fail_probe_notification;
+ }
+
+ for (i = 0; i < num_regions; i++) {
+ unsigned int id;
+ u64 start, size;
+
+ result = ps3_repository_read_stor_dev_region(repo->bus_index,
+ repo->dev_index,
+ i, &id, &start,
+ &size);
+ if (result) {
+ printk(KERN_ERR
+ "%s:%u: read_stor_dev_region failed %d\n",
+ __func__, __LINE__, result);
+ result = -ENODEV;
+ goto fail_read_region;
+ }
+ pr_debug("%s:%u: region %u: id %u start %lu size %lu\n",
+ __func__, __LINE__, i, id, start, size);
+
+ p->regions[i].id = id;
+ p->regions[i].start = start;
+ p->regions[i].size = size;
+ }
+
+ result = ps3_system_bus_device_register(&p->sbd);
+ if (result) {
+ pr_debug("%s:%u ps3_system_bus_device_register failed\n",
+ __func__, __LINE__);
+ goto fail_device_register;
+ }
+
+ pr_debug(" <- %s:%u\n", __func__, __LINE__);
+ return 0;
+
+fail_device_register:
+fail_read_region:
+fail_probe_notification:
+fail_find_interrupt:
+ kfree(p);
+fail_malloc:
+ pr_debug(" <- %s:%u: fail.\n", __func__, __LINE__);
+ return result;
+}
+
static int __init ps3_register_vuart_devices(void)
{
int result;
@@ -356,6 +613,35 @@ static int ps3_register_repository_devic
__func__, __LINE__);
}
break;
+ case PS3_DEV_TYPE_STOR_DISK:
+ result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_DISK);
+
+ /* Some devices are not accessable from the Other OS lpar. */
+ if (result == -ENODEV) {
+ result = 0;
+ pr_debug("%s:%u: not accessable\n", __func__,
+ __LINE__);
+ }
+
+ if (result)
+ pr_debug("%s:%u ps3_setup_storage_dev failed\n",
+ __func__, __LINE__);
+ break;
+
+ case PS3_DEV_TYPE_STOR_ROM:
+ result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_ROM);
+ if (result)
+ pr_debug("%s:%u ps3_setup_storage_dev failed\n",
+ __func__, __LINE__);
+ break;
+
+ case PS3_DEV_TYPE_STOR_FLASH:
+ result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_FLASH);
+ if (result)
+ pr_debug("%s:%u ps3_setup_storage_dev failed\n",
+ __func__, __LINE__);
+ break;
+
default:
result = 0;
pr_debug("%s:%u: unsupported dev_type %u\n", __func__, __LINE__,
--
With kind regards,
Geert Uytterhoeven
Software Architect
Sony Network and Software Technology Center Europe
The Corporate Village · Da Vincilaan 7-D1 · B-1935 Zaventem · Belgium
Phone: +32 (0)2 700 8453
Fax: +32 (0)2 700 8622
E-mail: Geert.Uytterhoeven@sonycom.com
Internet: http://www.sony-europe.com/
Sony Network and Software Technology Center Europe
A division of Sony Service Centre (Europe) N.V.
Registered office: Technologielaan 7 · B-1840 Londerzeel · Belgium
VAT BE 0413.825.160 · RPR Brussels
Fortis Bank Zaventem · Swift GEBABEBB08A · IBAN BE39001382358619
^ permalink raw reply
* [patch 6/6] ps3: FLASH ROM Storage Driver
From: Geert Uytterhoeven @ 2007-07-04 13:22 UTC (permalink / raw)
To: Paul Mackerras, Jens Axboe, James E.J. Bottomley,
Alessandro Rubini
Cc: Geert Uytterhoeven, linuxppc-dev, linux-kernel, linux-scsi
In-Reply-To: <20070704132212.726923000@pademelon.sonytel.be>
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 14401 bytes --]
From: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
Add a FLASH ROM Storage Driver for the PS3:
- Implemented as a misc character device driver
- Uses a fixed 256 KiB buffer allocated from boot memory as the hypervisor
requires the writing of aligned 256 KiB blocks
CC: Geoff Levand <geoffrey.levand@am.sony.com>
Signed-off-by: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
---
v3:
- Cleanup #includes
- Allow to disable ps3flash (and the preallocated 256 KiB buffer) using
`ps3flash=off'
- Move selection of write/read strings to error path
- Rename and move ps3stor_interrupt() to ps3flash_interrupt()
- Pass the interrupt handler to ps3stor_setup()
- Reset ps3flash_priv(dev) to NULL during cleanup
v2:
- Don't use `default y' in Kconfig
- #include <linux/uaccess.h> instead of <asm/uaccess.h>
- Set up sysfs links between misc character device and PS3 system device:
o /sys/class/misc/ps3flash/device -> ../../../devices/ps3_system/sb_01
o /sys/devices/ps3_system/sb_01/misc:ps3flash -> ../../../class/misc/ps3flash
arch/powerpc/platforms/ps3/Kconfig | 15 +
drivers/char/Makefile | 2
drivers/char/ps3flash.c | 429 +++++++++++++++++++++++++++++++++++++
3 files changed, 446 insertions(+)
--- a/arch/powerpc/platforms/ps3/Kconfig
+++ b/arch/powerpc/platforms/ps3/Kconfig
@@ -123,4 +123,19 @@ config PS3_ROM
In general, all users will say Y or M.
Also make sure to say Y or M to "SCSI CDROM support" later.
+config PS3_FLASH
+ tristate "PS3 FLASH ROM Storage Driver"
+ depends on PPC_PS3
+ select PS3_STORAGE
+ help
+ Include support for the PS3 FLASH ROM Storage.
+
+ This support is required to access the PS3 FLASH ROM, which
+ contains the boot loader and some boot options.
+ In general, all users will say Y or M.
+
+ As this driver needs a fixed buffer of 256 KiB of memory, it can
+ be disabled on the kernel command line using "ps3flash=off", to
+ not allocate this fixed buffer.
+
endmenu
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -104,6 +104,8 @@ obj-$(CONFIG_IPMI_HANDLER) += ipmi/
obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
obj-$(CONFIG_TCG_TPM) += tpm/
+obj-$(CONFIG_PS3_FLASH) += ps3flash.o
+
# Files generated that shall be removed upon make clean
clean-files := consolemap_deftbl.c defkeymap.c
--- /dev/null
+++ b/drivers/char/ps3flash.c
@@ -0,0 +1,429 @@
+/*
+ * PS3 FLASH ROM Storage Driver
+ *
+ * Copyright (C) 2007 Sony Computer Entertainment Inc.
+ * Copyright 2007 Sony Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+
+#include <asm/lv1call.h>
+#include <asm/ps3stor.h>
+
+
+#define DEVICE_NAME "ps3flash"
+
+#define FLASH_BLOCK_SIZE (256*1024)
+
+
+struct ps3flash_private {
+ struct mutex mutex; /* Bounce buffer mutex */
+};
+#define ps3flash_priv(dev) ((dev)->sbd.core.driver_data)
+
+static struct ps3_storage_device *ps3flash_dev;
+
+static ssize_t ps3flash_read_write_sectors(struct ps3_storage_device *dev,
+ u64 lpar, u64 start_sector,
+ u64 sectors, int write)
+{
+ u64 res = ps3stor_read_write_sectors(dev, lpar, start_sector, sectors,
+ write);
+ if (res) {
+ dev_err(&dev->sbd.core, "%s:%u: %s failed 0x%lx\n", __func__,
+ __LINE__, write ? "write" : "read", res);
+ return -EIO;
+ }
+ return sectors;
+}
+
+static ssize_t ps3flash_read_sectors(struct ps3_storage_device *dev,
+ u64 start_sector, u64 sectors,
+ unsigned int sector_offset)
+{
+ u64 max_sectors, lpar;
+
+ max_sectors = dev->bounce_size / dev->blk_size;
+ if (sectors > max_sectors) {
+ dev_dbg(&dev->sbd.core, "%s:%u Limiting sectors to %lu\n",
+ __func__, __LINE__, max_sectors);
+ sectors = max_sectors;
+ }
+
+ lpar = dev->bounce_lpar + sector_offset * dev->blk_size;
+ return ps3flash_read_write_sectors(dev, lpar, start_sector, sectors,
+ 0);
+}
+
+static ssize_t ps3flash_write_chunk(struct ps3_storage_device *dev,
+ u64 start_sector)
+{
+ u64 sectors = dev->bounce_size / dev->blk_size;
+ return ps3flash_read_write_sectors(dev, dev->bounce_lpar, start_sector,
+ sectors, 1);
+}
+
+static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin)
+{
+ struct ps3_storage_device *dev = ps3flash_dev;
+ u64 size = dev->regions[dev->region_idx].size*dev->blk_size;
+
+ switch (origin) {
+ case 1:
+ offset += file->f_pos;
+ break;
+ case 2:
+ offset += size;
+ break;
+ }
+ if (offset < 0)
+ return -EINVAL;
+
+ file->f_pos = offset;
+ return file->f_pos;
+}
+
+static ssize_t ps3flash_read(struct file *file, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct ps3_storage_device *dev = ps3flash_dev;
+ struct ps3flash_private *priv = ps3flash_priv(dev);
+ u64 size, start_sector, end_sector, offset;
+ ssize_t sectors_read;
+ size_t remaining, n;
+
+ dev_dbg(&dev->sbd.core,
+ "%s:%u: Reading %zu bytes at position %lld to user 0x%p\n",
+ __func__, __LINE__, count, *pos, buf);
+
+ size = dev->regions[dev->region_idx].size*dev->blk_size;
+ if (*pos >= size || !count)
+ return 0;
+
+ if (*pos+count > size) {
+ dev_dbg(&dev->sbd.core,
+ "%s:%u Truncating count from %zu to %llu\n", __func__,
+ __LINE__, count, size - *pos);
+ count = size - *pos;
+ }
+
+ start_sector = do_div_llr(*pos, dev->blk_size, &offset);
+ end_sector = DIV_ROUND_UP(*pos+count, dev->blk_size);
+
+ remaining = count;
+ do {
+ mutex_lock(&priv->mutex);
+
+ sectors_read = ps3flash_read_sectors(dev, start_sector,
+ end_sector-start_sector,
+ 0);
+ if (sectors_read < 0) {
+ mutex_unlock(&priv->mutex);
+ return sectors_read;
+ }
+
+ n = min(remaining, sectors_read*dev->blk_size-offset);
+ dev_dbg(&dev->sbd.core,
+ "%s:%u: copy %lu bytes from 0x%p to user 0x%p\n",
+ __func__, __LINE__, n, dev->bounce_buf+offset, buf);
+ if (copy_to_user(buf, dev->bounce_buf+offset, n)) {
+ mutex_unlock(&priv->mutex);
+ return -EFAULT;
+ }
+
+ mutex_unlock(&priv->mutex);
+
+ *pos += n;
+ buf += n;
+ remaining -= n;
+ start_sector += sectors_read;
+ offset = 0;
+ } while (remaining > 0);
+
+ return count;
+}
+
+static ssize_t ps3flash_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct ps3_storage_device *dev = ps3flash_dev;
+ struct ps3flash_private *priv = ps3flash_priv(dev);
+ u64 size, chunk_sectors, start_write_sector, end_write_sector,
+ end_read_sector, start_read_sector, head, tail, offset;
+ ssize_t res;
+ size_t remaining, n;
+ unsigned int sec_off;
+
+ dev_dbg(&dev->sbd.core,
+ "%s:%u: Writing %zu bytes at position %lld from user 0x%p\n",
+ __func__, __LINE__, count, *pos, buf);
+
+ size = dev->regions[dev->region_idx].size*dev->blk_size;
+ if (*pos >= size || !count)
+ return 0;
+
+ if (*pos+count > size) {
+ dev_dbg(&dev->sbd.core,
+ "%s:%u Truncating count from %zu to %llu\n", __func__,
+ __LINE__, count, size - *pos);
+ count = size - *pos;
+ }
+
+ chunk_sectors = dev->bounce_size / dev->blk_size;
+
+ start_write_sector = do_div_llr(*pos, dev->bounce_size, &offset) *
+ chunk_sectors;
+ end_write_sector = DIV_ROUND_UP(*pos+count, dev->bounce_size) *
+ chunk_sectors;
+
+ end_read_sector = DIV_ROUND_UP(*pos, dev->blk_size);
+ start_read_sector = (*pos+count) / dev->blk_size;
+
+ /*
+ * As we have to write in 256 KiB chunks, while we can read in blk_size
+ * (usually 512 bytes) chunks, we perform the following steps:
+ * 1. Read from start_write_sector to end_read_sector ("head")
+ * 2. Read from start_read_sector to end_write_sector ("tail")
+ * 3. Copy data to buffer
+ * 4. Write from start_write_sector to end_write_sector
+ * All of this is complicated by using only one 256 KiB bounce buffer.
+ */
+
+ head = end_read_sector-start_write_sector;
+ tail = end_write_sector-start_read_sector;
+
+ remaining = count;
+ do {
+ mutex_lock(&priv->mutex);
+
+ if (end_read_sector >= start_read_sector) {
+ /* Merge head and tail */
+ dev_dbg(&dev->sbd.core,
+ "Merged head and tail: %lu sectors at %lu\n",
+ chunk_sectors, start_write_sector);
+ res = ps3flash_read_sectors(dev, start_write_sector,
+ chunk_sectors, 0);
+ if (res < 0)
+ goto fail;
+ } else {
+ if (head) {
+ /* Read head */
+ dev_dbg(&dev->sbd.core,
+ "head: %lu sectors at %lu\n", head,
+ start_write_sector);
+ res = ps3flash_read_sectors(dev,
+ start_write_sector,
+ head, 0);
+ if (res < 0)
+ goto fail;
+ }
+ if (start_read_sector <
+ start_write_sector+chunk_sectors) {
+ /* Read tail */
+ dev_dbg(&dev->sbd.core,
+ "tail: %lu sectors at %lu\n", tail,
+ start_read_sector);
+ sec_off = start_read_sector-start_write_sector;
+ res = ps3flash_read_sectors(dev,
+ start_read_sector,
+ tail, sec_off);
+ if (res < 0)
+ goto fail;
+ }
+ }
+
+ n = min(remaining, dev->bounce_size-offset);
+ dev_dbg(&dev->sbd.core,
+ "%s:%u: copy %lu bytes from user 0x%p to 0x%p\n",
+ __func__, __LINE__, n, buf, dev->bounce_buf+offset);
+ if (copy_from_user(dev->bounce_buf+offset, buf, n)) {
+ res = -EFAULT;
+ goto fail;
+ }
+
+ res = ps3flash_write_chunk(dev, start_write_sector);
+ if (res < 0)
+ goto fail;
+
+ mutex_unlock(&priv->mutex);
+
+ *pos += n;
+ buf += n;
+ remaining -= n;
+ start_write_sector += chunk_sectors;
+ head = 0;
+ offset = 0;
+ } while (remaining > 0);
+
+ return count;
+
+fail:
+ mutex_unlock(&priv->mutex);
+ return res;
+}
+
+
+static irqreturn_t ps3flash_interrupt(int irq, void *data)
+{
+ struct ps3_storage_device *dev = data;
+ int res;
+ u64 tag, status;
+
+ res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status);
+
+ if (tag != dev->tag)
+ dev_err(&dev->sbd.core,
+ "%s:%u: tag mismatch, got %lx, expected %lx\n",
+ __func__, __LINE__, tag, dev->tag);
+
+ if (res) {
+ dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%lx\n",
+ __func__, __LINE__, res, status);
+ } else {
+ dev->lv1_status = status;
+ complete(&dev->done);
+ }
+ return IRQ_HANDLED;
+}
+
+
+static const struct file_operations ps3flash_fops = {
+ .owner = THIS_MODULE,
+ .llseek = ps3flash_llseek,
+ .read = ps3flash_read,
+ .write = ps3flash_write,
+};
+
+static struct miscdevice ps3flash_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = DEVICE_NAME,
+ .fops = &ps3flash_fops,
+};
+
+static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev)
+{
+ struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+ struct ps3flash_private *priv;
+ int error;
+ unsigned long tmp;
+
+ tmp = dev->regions[dev->region_idx].start*dev->blk_size;
+ if (tmp % FLASH_BLOCK_SIZE) {
+ dev_err(&dev->sbd.core,
+ "%s:%u region start %lu is not aligned\n", __func__,
+ __LINE__, tmp);
+ return -EINVAL;
+ }
+ tmp = dev->regions[dev->region_idx].size*dev->blk_size;
+ if (tmp % FLASH_BLOCK_SIZE) {
+ dev_err(&dev->sbd.core,
+ "%s:%u region size %lu is not aligned\n", __func__,
+ __LINE__, tmp);
+ return -EINVAL;
+ }
+
+ /* use static buffer, kmalloc cannot allocate 256 KiB */
+ if (!ps3flash_bounce_buffer.address)
+ return -ENODEV;
+
+ if (ps3flash_dev) {
+ dev_err(&dev->sbd.core,
+ "Only one FLASH device is supported\n");
+ return -EBUSY;
+ }
+
+ ps3flash_dev = dev;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ error = -ENOMEM;
+ goto fail;
+ }
+
+ ps3flash_priv(dev) = priv;
+ mutex_init(&priv->mutex);
+
+ dev->bounce_size = ps3flash_bounce_buffer.size;
+ dev->bounce_buf = ps3flash_bounce_buffer.address;
+
+ error = ps3stor_setup(dev, ps3flash_interrupt);
+ if (error)
+ goto fail_free_priv;
+
+ ps3flash_misc.parent = &dev->sbd.core;
+ error = misc_register(&ps3flash_misc);
+ if (error) {
+ dev_err(&dev->sbd.core, "%s:%u: misc_register failed %d\n",
+ __func__, __LINE__, error);
+ goto fail_teardown;
+ }
+
+ dev_info(&dev->sbd.core, "%s:%u: registered misc device %d\n",
+ __func__, __LINE__, ps3flash_misc.minor);
+ return 0;
+
+fail_teardown:
+ ps3stor_teardown(dev);
+fail_free_priv:
+ kfree(priv);
+ ps3flash_priv(dev) = NULL;
+fail:
+ ps3flash_dev = NULL;
+ return error;
+}
+
+static int ps3flash_remove(struct ps3_system_bus_device *_dev)
+{
+ struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+
+ misc_deregister(&ps3flash_misc);
+ ps3stor_teardown(dev);
+ kfree(ps3flash_priv(dev));
+ ps3flash_priv(dev) = NULL;
+ ps3flash_dev = NULL;
+ return 0;
+}
+
+
+static struct ps3_system_bus_driver ps3flash = {
+ .match_id = PS3_MATCH_ID_STOR_FLASH,
+ .core.name = DEVICE_NAME,
+ .core.owner = THIS_MODULE,
+ .probe = ps3flash_probe,
+ .remove = ps3flash_remove,
+ .shutdown = ps3flash_remove,
+};
+
+
+static int __init ps3flash_init(void)
+{
+ return ps3_system_bus_driver_register(&ps3flash);
+}
+
+static void __exit ps3flash_exit(void)
+{
+ ps3_system_bus_driver_unregister(&ps3flash);
+}
+
+module_init(ps3flash_init);
+module_exit(ps3flash_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PS3 FLASH ROM Storage Driver");
+MODULE_AUTHOR("Sony Corporation");
+MODULE_ALIAS(PS3_MODULE_ALIAS_STOR_FLASH);
--
With kind regards,
Geert Uytterhoeven
Software Architect
Sony Network and Software Technology Center Europe
The Corporate Village · Da Vincilaan 7-D1 · B-1935 Zaventem · Belgium
Phone: +32 (0)2 700 8453
Fax: +32 (0)2 700 8622
E-mail: Geert.Uytterhoeven@sonycom.com
Internet: http://www.sony-europe.com/
Sony Network and Software Technology Center Europe
A division of Sony Service Centre (Europe) N.V.
Registered office: Technologielaan 7 · B-1840 Londerzeel · Belgium
VAT BE 0413.825.160 · RPR Brussels
Fortis Bank Zaventem · Swift GEBABEBB08A · IBAN BE39001382358619
^ permalink raw reply
* [patch 5/6] ps3: BD/DVD/CD-ROM Storage Driver
From: Geert Uytterhoeven @ 2007-07-04 13:22 UTC (permalink / raw)
To: Paul Mackerras, Jens Axboe, James E.J. Bottomley,
Alessandro Rubini
Cc: Geert Uytterhoeven, linuxppc-dev, linux-kernel, linux-scsi
In-Reply-To: <20070704132212.726923000@pademelon.sonytel.be>
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 17658 bytes --]
From: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
Add a BD/DVD/CD-ROM Storage Driver for the PS3:
- Implemented as a SCSI device driver
- Uses software scatter-gather with a 64 KiB bounce buffer as the hypervisor
doesn't support scatter-gather
CC: Geoff Levand <geoffrey.levand@am.sony.com>
Signed-off-by: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
---
v3:
- Replace dependency on BLK_DEV_SR by SCSI
- Use `BD/DVD/CD-ROM' instead of `ROM' in driver descriptions
- Inform the user to enable "SCSI CDROM support"
- Cleanup #includes
- Group LV1 API related stuff
- Rename ps3rom_private.cmd to ps3rom_private.curr_cmd
- Use shost_priv() (from scsi-misc)
- Set scsi_device.use_10_for_rw and remove support for {READ,WRITE}_6
- Explain why we prefer to use lv1_storage_{read,write}()
- Pass the interrupt handler to ps3stor_setup(), so it doesn't have to be
overridden later
- Reset ps3rom_priv(dev) to NULL during cleanup
v2:
- Don't use `default y' in Kconfig
- Add missing #include <linux/highmem.h>
- Remove ps3rom_private.scsi_done, use scsi_cmnd.scsi_done instead
- Use scsi_device.host.hostdata
- Remove empty ps3rom_slave_{alloc,destroy}()
- Kill superfluous test for command in progress
- Move scsi_host_put() last in cleanup sequence
- Remove scsi_command(), use scsi_print_command() for debugging
- scsi_cmnd.use_sg is always > 0 these days
- Allocate struct ps3rom_private using scsi_host_alloc()
- Remove the thread for handling requests
- Remove unused position parameter enum
- Remove unused NA_PROTO and DIR_NA
- Derive buffer length, data direction, and transfer protocol from the
struct scsi_command, instead of using a big switch() statement
- Kill superfluous spinlock
- Remove manual request sense, as modern hypervisors always do auto sense
- Pass all SCSI commands to the hypervisor as ATAPI commands, except for
READ_*/WRITE_*
- Don't print errors for SCSI commands that are not allowed for an Other OS
by the hypervisor
- Remove superfluous tests for data directions in
{fill_from,fetch_to}_dev_buffer()
- Handle errors in {fill_from,fetch_to}_dev_buffer()
- Reorder routines
- Manually inline ps3rom_send_atapi_command()
- Fix all FIXMEs
arch/powerpc/platforms/ps3/Kconfig | 11
drivers/scsi/Makefile | 1
drivers/scsi/ps3rom.c | 537 +++++++++++++++++++++++++++++++++++++
3 files changed, 549 insertions(+)
--- a/arch/powerpc/platforms/ps3/Kconfig
+++ b/arch/powerpc/platforms/ps3/Kconfig
@@ -112,4 +112,15 @@ config PS3_DISK
This support is required to access the PS3 hard disk.
In general, all users will say Y or M.
+config PS3_ROM
+ tristate "PS3 BD/DVD/CD-ROM Storage Driver"
+ depends on PPC_PS3 && SCSI
+ select PS3_STORAGE
+ help
+ Include support for the PS3 ROM Storage.
+
+ This support is required to access the PS3 BD/DVD/CD-ROM drive.
+ In general, all users will say Y or M.
+ Also make sure to say Y or M to "SCSI CDROM support" later.
+
endmenu
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -132,6 +132,7 @@ obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsi/
obj-$(CONFIG_SCSI_IBMVSCSIS) += ibmvscsi/
obj-$(CONFIG_SCSI_HPTIOP) += hptiop.o
obj-$(CONFIG_SCSI_STEX) += stex.o
+obj-$(CONFIG_PS3_ROM) += ps3rom.o
obj-$(CONFIG_ARM) += arm/
--- /dev/null
+++ b/drivers/scsi/ps3rom.c
@@ -0,0 +1,537 @@
+/*
+ * PS3 BD/DVD/CD-ROM Storage Driver
+ *
+ * Copyright (C) 2007 Sony Computer Entertainment Inc.
+ * Copyright 2007 Sony Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/cdrom.h>
+#include <linux/highmem.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+#include <asm/lv1call.h>
+#include <asm/ps3stor.h>
+
+
+#define DEVICE_NAME "ps3rom"
+
+#define BOUNCE_SIZE (64*1024)
+
+#define PS3ROM_MAX_SECTORS (BOUNCE_SIZE / CD_FRAMESIZE)
+
+
+struct ps3rom_private {
+ struct ps3_storage_device *dev;
+ struct scsi_cmnd *curr_cmd;
+};
+#define ps3rom_priv(dev) ((dev)->sbd.core.driver_data)
+
+
+#define LV1_STORAGE_SEND_ATAPI_COMMAND (1)
+
+struct lv1_atapi_cmnd_block {
+ u8 pkt[32]; /* packet command block */
+ u32 pktlen; /* should be 12 for ATAPI 8020 */
+ u32 blocks;
+ u32 block_size;
+ u32 proto; /* transfer mode */
+ u32 in_out; /* transfer direction */
+ u64 buffer; /* parameter except command block */
+ u32 arglen; /* length above */
+};
+
+enum lv1_atapi_proto {
+ NON_DATA_PROTO = 0,
+ PIO_DATA_IN_PROTO = 1,
+ PIO_DATA_OUT_PROTO = 2,
+ DMA_PROTO = 3
+};
+
+enum lv1_atapi_in_out {
+ DIR_WRITE = 0, /* memory -> device */
+ DIR_READ = 1 /* device -> memory */
+};
+
+
+static int ps3rom_slave_configure(struct scsi_device *scsi_dev)
+{
+ struct ps3rom_private *priv = shost_priv(scsi_dev->host);
+ struct ps3_storage_device *dev = priv->dev;
+
+ dev_dbg(&dev->sbd.core, "%s:%u: id %u, lun %u, channel %u\n", __func__,
+ __LINE__, scsi_dev->id, scsi_dev->lun, scsi_dev->channel);
+
+ /*
+ * ATAPI SFF8020 devices use MODE_SENSE_10,
+ * so we can prohibit MODE_SENSE_6
+ */
+ scsi_dev->use_10_for_ms = 1;
+
+ /* we don't support {READ,WRITE}_6 */
+ scsi_dev->use_10_for_rw = 1;
+
+ return 0;
+}
+
+/*
+ * copy data from device into scatter/gather buffer
+ */
+static int fill_from_dev_buffer(struct scsi_cmnd *cmd, const void *buf)
+{
+ int k, req_len, act_len, len, active;
+ void *kaddr;
+ struct scatterlist *sgpnt;
+ unsigned int buflen;
+
+ buflen = cmd->request_bufflen;
+ if (!buflen)
+ return 0;
+
+ if (!cmd->request_buffer)
+ return -1;
+
+ sgpnt = cmd->request_buffer;
+ active = 1;
+ for (k = 0, req_len = 0, act_len = 0; k < cmd->use_sg; ++k, ++sgpnt) {
+ if (active) {
+ kaddr = kmap_atomic(sgpnt->page, KM_USER0);
+ if (!kaddr)
+ return -1;
+ len = sgpnt->length;
+ if ((req_len + len) > buflen) {
+ active = 0;
+ len = buflen - req_len;
+ }
+ memcpy(kaddr + sgpnt->offset, buf + req_len, len);
+ kunmap_atomic(kaddr, KM_USER0);
+ act_len += len;
+ }
+ req_len += sgpnt->length;
+ }
+ cmd->resid = req_len - act_len;
+ return 0;
+}
+
+/*
+ * copy data from scatter/gather into device's buffer
+ */
+static int fetch_to_dev_buffer(struct scsi_cmnd *cmd, void *buf)
+{
+ int k, req_len, len, fin;
+ void *kaddr;
+ struct scatterlist *sgpnt;
+ unsigned int buflen;
+
+ buflen = cmd->request_bufflen;
+ if (!buflen)
+ return 0;
+
+ if (!cmd->request_buffer)
+ return -1;
+
+ sgpnt = cmd->request_buffer;
+ for (k = 0, req_len = 0, fin = 0; k < cmd->use_sg; ++k, ++sgpnt) {
+ kaddr = kmap_atomic(sgpnt->page, KM_USER0);
+ if (!kaddr)
+ return -1;
+ len = sgpnt->length;
+ if ((req_len + len) > buflen) {
+ len = buflen - req_len;
+ fin = 1;
+ }
+ memcpy(buf + req_len, kaddr + sgpnt->offset, len);
+ kunmap_atomic(kaddr, KM_USER0);
+ if (fin)
+ return req_len + len;
+ req_len += sgpnt->length;
+ }
+ return req_len;
+}
+
+static int ps3rom_atapi_request(struct ps3_storage_device *dev,
+ struct scsi_cmnd *cmd)
+{
+ struct lv1_atapi_cmnd_block atapi_cmnd;
+ unsigned char opcode = cmd->cmnd[0];
+ int res;
+ u64 lpar;
+
+ dev_dbg(&dev->sbd.core, "%s:%u: send ATAPI command 0x%02x\n", __func__,
+ __LINE__, opcode);
+
+ memset(&atapi_cmnd, 0, sizeof(struct lv1_atapi_cmnd_block));
+ memcpy(&atapi_cmnd.pkt, cmd->cmnd, 12);
+ atapi_cmnd.pktlen = 12;
+ atapi_cmnd.block_size = 1; /* transfer size is block_size * blocks */
+ atapi_cmnd.blocks = atapi_cmnd.arglen = cmd->request_bufflen;
+ atapi_cmnd.buffer = dev->bounce_lpar;
+
+ switch (cmd->sc_data_direction) {
+ case DMA_FROM_DEVICE:
+ if (cmd->request_bufflen >= CD_FRAMESIZE)
+ atapi_cmnd.proto = DMA_PROTO;
+ else
+ atapi_cmnd.proto = PIO_DATA_IN_PROTO;
+ atapi_cmnd.in_out = DIR_READ;
+ break;
+
+ case DMA_TO_DEVICE:
+ if (cmd->request_bufflen >= CD_FRAMESIZE)
+ atapi_cmnd.proto = DMA_PROTO;
+ else
+ atapi_cmnd.proto = PIO_DATA_OUT_PROTO;
+ atapi_cmnd.in_out = DIR_WRITE;
+ res = fetch_to_dev_buffer(cmd, dev->bounce_buf);
+ if (res < 0)
+ return DID_ERROR << 16;
+ break;
+
+ default:
+ atapi_cmnd.proto = NON_DATA_PROTO;
+ break;
+ }
+
+ lpar = ps3_mm_phys_to_lpar(__pa(&atapi_cmnd));
+ res = lv1_storage_send_device_command(dev->sbd.dev_id,
+ LV1_STORAGE_SEND_ATAPI_COMMAND,
+ lpar, sizeof(atapi_cmnd),
+ atapi_cmnd.buffer,
+ atapi_cmnd.arglen, &dev->tag);
+ if (res == LV1_DENIED_BY_POLICY) {
+ dev_dbg(&dev->sbd.core,
+ "%s:%u: ATAPI command 0x%02x denied by policy\n",
+ __func__, __LINE__, opcode);
+ return DID_ERROR << 16;
+ }
+
+ if (res) {
+ dev_err(&dev->sbd.core,
+ "%s:%u: ATAPI command 0x%02x failed %d\n", __func__,
+ __LINE__, opcode, res);
+ return DID_ERROR << 16;
+ }
+
+ return 0;
+}
+
+static inline unsigned int srb10_lba(const struct scsi_cmnd *cmd)
+{
+ return cmd->cmnd[2] << 24 | cmd->cmnd[3] << 16 | cmd->cmnd[4] << 8 |
+ cmd->cmnd[5];
+}
+
+static inline unsigned int srb10_len(const struct scsi_cmnd *cmd)
+{
+ return cmd->cmnd[7] << 8 | cmd->cmnd[8];
+}
+
+static int ps3rom_read_request(struct ps3_storage_device *dev,
+ struct scsi_cmnd *cmd, u32 start_sector,
+ u32 sectors)
+{
+ int res;
+
+ dev_dbg(&dev->sbd.core, "%s:%u: read %u sectors starting at %u\n",
+ __func__, __LINE__, sectors, start_sector);
+
+ res = lv1_storage_read(dev->sbd.dev_id,
+ dev->regions[dev->region_idx].id, start_sector,
+ sectors, 0, dev->bounce_lpar, &dev->tag);
+ if (res) {
+ dev_err(&dev->sbd.core, "%s:%u: read failed %d\n", __func__,
+ __LINE__, res);
+ return DID_ERROR << 16;
+ }
+
+ return 0;
+}
+
+static int ps3rom_write_request(struct ps3_storage_device *dev,
+ struct scsi_cmnd *cmd, u32 start_sector,
+ u32 sectors)
+{
+ int res;
+
+ dev_dbg(&dev->sbd.core, "%s:%u: write %u sectors starting at %u\n",
+ __func__, __LINE__, sectors, start_sector);
+
+ res = fetch_to_dev_buffer(cmd, dev->bounce_buf);
+ if (res < 0)
+ return DID_ERROR << 16;
+
+ res = lv1_storage_write(dev->sbd.dev_id,
+ dev->regions[dev->region_idx].id, start_sector,
+ sectors, 0, dev->bounce_lpar, &dev->tag);
+ if (res) {
+ dev_err(&dev->sbd.core, "%s:%u: write failed %d\n", __func__,
+ __LINE__, res);
+ return DID_ERROR << 16;
+ }
+
+ return 0;
+}
+
+static int ps3rom_queuecommand(struct scsi_cmnd *cmd,
+ void (*done)(struct scsi_cmnd *))
+{
+ struct ps3rom_private *priv = shost_priv(cmd->device->host);
+ struct ps3_storage_device *dev = priv->dev;
+ unsigned char opcode;
+ int res;
+
+#ifdef DEBUG
+ scsi_print_command(cmd);
+#endif
+
+ priv->curr_cmd = cmd;
+ cmd->scsi_done = done;
+
+ opcode = cmd->cmnd[0];
+ /*
+ * While we can submit READ/WRITE SCSI commands as ATAPI commands,
+ * it's recommended for various reasons (performance, error handling,
+ * ...) to use lv1_storage_{read,write}() instead
+ */
+ switch (opcode) {
+ case READ_10:
+ res = ps3rom_read_request(dev, cmd, srb10_lba(cmd),
+ srb10_len(cmd));
+ break;
+
+ case WRITE_10:
+ res = ps3rom_write_request(dev, cmd, srb10_lba(cmd),
+ srb10_len(cmd));
+ break;
+
+ default:
+ res = ps3rom_atapi_request(dev, cmd);
+ break;
+ }
+
+ if (res) {
+ memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
+ cmd->result = res;
+ cmd->sense_buffer[0] = 0x70;
+ cmd->sense_buffer[2] = ILLEGAL_REQUEST;
+ priv->curr_cmd = NULL;
+ cmd->scsi_done(cmd);
+ }
+
+ return 0;
+}
+
+static int decode_lv1_status(u64 status, unsigned char *sense_key,
+ unsigned char *asc, unsigned char *ascq)
+{
+ if (((status >> 24) & 0xff) != SAM_STAT_CHECK_CONDITION)
+ return -1;
+
+ *sense_key = (status >> 16) & 0xff;
+ *asc = (status >> 8) & 0xff;
+ *ascq = status & 0xff;
+ return 0;
+}
+
+static irqreturn_t ps3rom_interrupt(int irq, void *data)
+{
+ struct ps3_storage_device *dev = data;
+ struct Scsi_Host *host;
+ struct ps3rom_private *priv;
+ struct scsi_cmnd *cmd;
+ int res;
+ u64 tag, status;
+ unsigned char sense_key, asc, ascq;
+
+ res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status);
+ /*
+ * status = -1 may mean that ATAPI transport completed OK, but
+ * ATAPI command itself resulted CHECK CONDITION
+ * so, upper layer should issue REQUEST_SENSE to check the sense data
+ */
+
+ if (tag != dev->tag)
+ dev_err(&dev->sbd.core,
+ "%s:%u: tag mismatch, got %lx, expected %lx\n",
+ __func__, __LINE__, tag, dev->tag);
+
+ if (res) {
+ dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%lx\n",
+ __func__, __LINE__, res, status);
+ return IRQ_HANDLED;
+ }
+
+ host = ps3rom_priv(dev);
+ priv = shost_priv(host);
+ cmd = priv->curr_cmd;
+
+ if (!status) {
+ /* OK, completed */
+ if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
+ res = fill_from_dev_buffer(cmd, dev->bounce_buf);
+ if (res) {
+ cmd->result = DID_ERROR << 16;
+ goto done;
+ }
+ }
+ cmd->result = DID_OK << 16;
+ goto done;
+ }
+
+ if (cmd->cmnd[0] == REQUEST_SENSE) {
+ /* SCSI spec says request sense should never get error */
+ dev_err(&dev->sbd.core, "%s:%u: end error without autosense\n",
+ __func__, __LINE__);
+ cmd->result = DID_ERROR << 16 | SAM_STAT_CHECK_CONDITION;
+ goto done;
+ }
+
+ if (decode_lv1_status(status, &sense_key, &asc, &ascq)) {
+ cmd->result = DID_ERROR << 16;
+ goto done;
+ }
+
+ cmd->sense_buffer[0] = 0x70;
+ cmd->sense_buffer[2] = sense_key;
+ cmd->sense_buffer[7] = 16 - 6;
+ cmd->sense_buffer[12] = asc;
+ cmd->sense_buffer[13] = ascq;
+ cmd->result = SAM_STAT_CHECK_CONDITION;
+
+done:
+ priv->curr_cmd = NULL;
+ cmd->scsi_done(cmd);
+ return IRQ_HANDLED;
+}
+
+static struct scsi_host_template ps3rom_host_template = {
+ .name = DEVICE_NAME,
+ .slave_configure = ps3rom_slave_configure,
+ .queuecommand = ps3rom_queuecommand,
+ .can_queue = 1,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .emulated = 1, /* only sg driver uses this */
+ .max_sectors = PS3ROM_MAX_SECTORS,
+ .use_clustering = ENABLE_CLUSTERING,
+ .module = THIS_MODULE,
+};
+
+
+static int __devinit ps3rom_probe(struct ps3_system_bus_device *_dev)
+{
+ struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+ int error;
+ struct Scsi_Host *host;
+ struct ps3rom_private *priv;
+
+ if (dev->blk_size != CD_FRAMESIZE) {
+ dev_err(&dev->sbd.core,
+ "%s:%u: cannot handle block size %lu\n", __func__,
+ __LINE__, dev->blk_size);
+ return -EINVAL;
+ }
+
+ dev->bounce_size = BOUNCE_SIZE;
+ dev->bounce_buf = kmalloc(BOUNCE_SIZE, GFP_DMA);
+ if (!dev->bounce_buf)
+ return -ENOMEM;
+
+ error = ps3stor_setup(dev, ps3rom_interrupt);
+ if (error)
+ goto fail_free_bounce;
+
+ host = scsi_host_alloc(&ps3rom_host_template,
+ sizeof(struct ps3rom_private));
+ if (!host) {
+ dev_err(&dev->sbd.core, "%s:%u: scsi_host_alloc failed\n",
+ __func__, __LINE__);
+ goto fail_teardown;
+ }
+
+ priv = shost_priv(host);
+ ps3rom_priv(dev) = host;
+ priv->dev = dev;
+
+ /* One device/LUN per SCSI bus */
+ host->max_id = 1;
+ host->max_lun = 1;
+
+ error = scsi_add_host(host, &dev->sbd.core);
+ if (error) {
+ dev_err(&dev->sbd.core, "%s:%u: scsi_host_alloc failed %d\n",
+ __func__, __LINE__, error);
+ error = -ENODEV;
+ goto fail_host_put;
+ }
+
+ scsi_scan_host(host);
+ return 0;
+
+fail_host_put:
+ scsi_host_put(host);
+ ps3rom_priv(dev) = NULL;
+fail_teardown:
+ ps3stor_teardown(dev);
+fail_free_bounce:
+ kfree(dev->bounce_buf);
+ return error;
+}
+
+static int ps3rom_remove(struct ps3_system_bus_device *_dev)
+{
+ struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+ struct Scsi_Host *host = ps3rom_priv(dev);
+
+ scsi_remove_host(host);
+ ps3stor_teardown(dev);
+ scsi_host_put(host);
+ ps3rom_priv(dev) = NULL;
+ kfree(dev->bounce_buf);
+ return 0;
+}
+
+static struct ps3_system_bus_driver ps3rom = {
+ .match_id = PS3_MATCH_ID_STOR_ROM,
+ .core.name = DEVICE_NAME,
+ .core.owner = THIS_MODULE,
+ .probe = ps3rom_probe,
+ .remove = ps3rom_remove
+};
+
+
+static int __init ps3rom_init(void)
+{
+ return ps3_system_bus_driver_register(&ps3rom);
+}
+
+static void __exit ps3rom_exit(void)
+{
+ ps3_system_bus_driver_unregister(&ps3rom);
+}
+
+module_init(ps3rom_init);
+module_exit(ps3rom_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PS3 BD/DVD/CD-ROM Storage Driver");
+MODULE_AUTHOR("Sony Corporation");
+MODULE_ALIAS(PS3_MODULE_ALIAS_STOR_ROM);
--
With kind regards,
Geert Uytterhoeven
Software Architect
Sony Network and Software Technology Center Europe
The Corporate Village · Da Vincilaan 7-D1 · B-1935 Zaventem · Belgium
Phone: +32 (0)2 700 8453
Fax: +32 (0)2 700 8622
E-mail: Geert.Uytterhoeven@sonycom.com
Internet: http://www.sony-europe.com/
Sony Network and Software Technology Center Europe
A division of Sony Service Centre (Europe) N.V.
Registered office: Technologielaan 7 · B-1840 Londerzeel · Belgium
VAT BE 0413.825.160 · RPR Brussels
Fortis Bank Zaventem · Swift GEBABEBB08A · IBAN BE39001382358619
^ permalink raw reply
* [patch 2/6] ps3: Storage Driver Core
From: Geert Uytterhoeven @ 2007-07-04 13:22 UTC (permalink / raw)
To: Paul Mackerras, Jens Axboe, James E.J. Bottomley,
Alessandro Rubini
Cc: Geert Uytterhoeven, linuxppc-dev, linux-kernel, linux-scsi
In-Reply-To: <20070704132212.726923000@pademelon.sonytel.be>
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 13048 bytes --]
From: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
Add storage driver core support for the PS3.
PS3 storage devices are a special kind of PS3 system bus devices
Signed-off-by: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>
---
v3:
- Rename and move ps3stor_interrupt() to ps3flash_interrupt()
- Add an interrupt handler parameter to ps3stor_setup(), so it doesn't have
to be overridden later
v2:
- Remove the `name' parameters of ps3stor_setup(), as it can be obtained from
the driver structure
- Cleanup struct ps3_storage_device
arch/powerpc/platforms/ps3/Kconfig | 4
drivers/ps3/Makefile | 1
drivers/ps3/ps3stor_lib.c | 302 +++++++++++++++++++++++++++++++++++++
include/asm-powerpc/ps3stor.h | 71 ++++++++
4 files changed, 378 insertions(+)
--- a/arch/powerpc/platforms/ps3/Kconfig
+++ b/arch/powerpc/platforms/ps3/Kconfig
@@ -98,4 +98,8 @@ config PS3_SYS_MANAGER
This support is required for system control. In
general, all users will say Y or M.
+config PS3_STORAGE
+ depends on PPC_PS3
+ tristate
+
endmenu
--- a/drivers/ps3/Makefile
+++ b/drivers/ps3/Makefile
@@ -3,3 +3,4 @@ obj-$(CONFIG_PS3_PS3AV) += ps3av_mod.o
ps3av_mod-objs += ps3av.o ps3av_cmd.o
obj-$(CONFIG_PPC_PS3) += sys-manager-core.o
obj-$(CONFIG_PS3_SYS_MANAGER) += sys-manager.o
+obj-$(CONFIG_PS3_STORAGE) += ps3stor_lib.o
--- /dev/null
+++ b/drivers/ps3/ps3stor_lib.c
@@ -0,0 +1,302 @@
+/*
+ * PS3 Storage Library
+ *
+ * Copyright (C) 2007 Sony Computer Entertainment Inc.
+ * Copyright 2007 Sony Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/dma-mapping.h>
+
+#include <asm/lv1call.h>
+#include <asm/ps3stor.h>
+
+
+static int ps3stor_probe_access(struct ps3_storage_device *dev)
+{
+ int res, error;
+ unsigned int i;
+ unsigned long n;
+
+ if (dev->sbd.match_id == PS3_MATCH_ID_STOR_ROM) {
+ /* special case: CD-ROM is assumed always accessible */
+ dev->accessible_regions = 1;
+ return 0;
+ }
+
+ error = -EPERM;
+ for (i = 0; i < dev->num_regions; i++) {
+ dev_dbg(&dev->sbd.core,
+ "%s:%u: checking accessibility of region %u\n",
+ __func__, __LINE__, i);
+
+ dev->region_idx = i;
+ res = ps3stor_read_write_sectors(dev, dev->bounce_lpar, 0, 1,
+ 0);
+ if (res) {
+ dev_dbg(&dev->sbd.core, "%s:%u: read failed, "
+ "region %u is not accessible\n", __func__,
+ __LINE__, i);
+ continue;
+ }
+
+ dev_dbg(&dev->sbd.core, "%s:%u: region %u is accessible\n",
+ __func__, __LINE__, i);
+ set_bit(i, &dev->accessible_regions);
+
+ /* We can access at least one region */
+ error = 0;
+ }
+ if (error)
+ return error;
+
+ n = hweight_long(dev->accessible_regions);
+ if (n > 1)
+ dev_info(&dev->sbd.core,
+ "%s:%u: %lu accessible regions found. Only the first "
+ "one will be used",
+ __func__, __LINE__, n);
+ dev->region_idx = __ffs(dev->accessible_regions);
+ dev_info(&dev->sbd.core,
+ "First accessible region has index %u start %lu size %lu\n",
+ dev->region_idx, dev->regions[dev->region_idx].start,
+ dev->regions[dev->region_idx].size);
+
+ return 0;
+}
+
+
+/**
+ * ps3stor_setup - Setup a storage device before use
+ * @dev: Pointer to a struct ps3_storage_device
+ * @handler: Pointer to an interrupt handler
+ *
+ * Returns 0 for success, or an error code
+ */
+int ps3stor_setup(struct ps3_storage_device *dev, irq_handler_t handler)
+{
+ int error, res, alignment;
+ enum ps3_dma_page_size page_size;
+
+ error = ps3_open_hv_device(&dev->sbd);
+ if (error) {
+ dev_err(&dev->sbd.core,
+ "%s:%u: ps3_open_hv_device failed %d\n", __func__,
+ __LINE__, error);
+ goto fail;
+ }
+
+ error = ps3_sb_event_receive_port_setup(&dev->sbd, PS3_BINDING_CPU_ANY,
+ &dev->irq);
+ if (error) {
+ dev_err(&dev->sbd.core,
+ "%s:%u: ps3_sb_event_receive_port_setup failed %d\n",
+ __func__, __LINE__, error);
+ goto fail_close_device;
+ }
+
+ error = request_irq(dev->irq, handler, IRQF_DISABLED,
+ dev->sbd.core.driver->name, dev);
+ if (error) {
+ dev_err(&dev->sbd.core, "%s:%u: request_irq failed %d\n",
+ __func__, __LINE__, error);
+ goto fail_sb_event_receive_port_destroy;
+ }
+
+ alignment = min(__ffs(dev->bounce_size),
+ __ffs((unsigned long)dev->bounce_buf));
+ if (alignment < 12) {
+ dev_err(&dev->sbd.core,
+ "%s:%u: bounce buffer not aligned (%lx at 0x%p)\n",
+ __func__, __LINE__, dev->bounce_size, dev->bounce_buf);
+ error = -EINVAL;
+ goto fail_free_irq;
+ } else if (alignment < 16)
+ page_size = PS3_DMA_4K;
+ else
+ page_size = PS3_DMA_64K;
+ dev->sbd.d_region = &dev->dma_region;
+ ps3_dma_region_init(&dev->sbd, &dev->dma_region, page_size,
+ PS3_DMA_OTHER, dev->bounce_buf, dev->bounce_size);
+ res = ps3_dma_region_create(&dev->dma_region);
+ if (res) {
+ dev_err(&dev->sbd.core, "%s:%u: cannot create DMA region\n",
+ __func__, __LINE__);
+ error = -ENOMEM;
+ goto fail_free_irq;
+ }
+
+ dev->bounce_lpar = ps3_mm_phys_to_lpar(__pa(dev->bounce_buf));
+ dev->bounce_dma = dma_map_single(&dev->sbd.core, dev->bounce_buf,
+ dev->bounce_size, DMA_BIDIRECTIONAL);
+ if (!dev->bounce_dma) {
+ dev_err(&dev->sbd.core, "%s:%u: map DMA region failed\n",
+ __func__, __LINE__);
+ error = -ENODEV;
+ goto fail_free_dma;
+ }
+
+ error = ps3stor_probe_access(dev);
+ if (error) {
+ dev_err(&dev->sbd.core, "%s:%u: No accessible regions found\n",
+ __func__, __LINE__);
+ goto fail_unmap_dma;
+ }
+ return 0;
+
+fail_unmap_dma:
+ dma_unmap_single(&dev->sbd.core, dev->bounce_dma, dev->bounce_size,
+ DMA_BIDIRECTIONAL);
+fail_free_dma:
+ ps3_dma_region_free(&dev->dma_region);
+fail_free_irq:
+ free_irq(dev->irq, dev);
+fail_sb_event_receive_port_destroy:
+ ps3_sb_event_receive_port_destroy(&dev->sbd, dev->irq);
+fail_close_device:
+ ps3_close_hv_device(&dev->sbd);
+fail:
+ return error;
+}
+EXPORT_SYMBOL_GPL(ps3stor_setup);
+
+
+/**
+ * ps3stor_teardown - Tear down a storage device after use
+ * @dev: Pointer to a struct ps3_storage_device
+ */
+void ps3stor_teardown(struct ps3_storage_device *dev)
+{
+ int error;
+
+ dma_unmap_single(&dev->sbd.core, dev->bounce_dma, dev->bounce_size,
+ DMA_BIDIRECTIONAL);
+ ps3_dma_region_free(&dev->dma_region);
+
+ free_irq(dev->irq, dev);
+
+ error = ps3_sb_event_receive_port_destroy(&dev->sbd, dev->irq);
+ if (error)
+ dev_err(&dev->sbd.core,
+ "%s:%u: destroy event receive port failed %d\n",
+ __func__, __LINE__, error);
+
+ error = ps3_close_hv_device(&dev->sbd);
+ if (error)
+ dev_err(&dev->sbd.core,
+ "%s:%u: ps3_close_hv_device failed %d\n", __func__,
+ __LINE__, error);
+}
+EXPORT_SYMBOL_GPL(ps3stor_teardown);
+
+
+/**
+ * ps3stor_read_write_sectors - read/write from/to a storage device
+ * @dev: Pointer to a struct ps3_storage_device
+ * @lpar: HV logical partition address
+ * @start_sector: First sector to read/write
+ * @sectors: Number of sectors to read/write
+ * @write: Flag indicating write (non-zero) or read (zero)
+ *
+ * Returns 0 for success, -1 in case of failure to submit the command, or
+ * an LV1 status value in case of other errors
+ */
+u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar,
+ u64 start_sector, u64 sectors, int write)
+{
+ unsigned int region_id = dev->regions[dev->region_idx].id;
+ const char *op = write ? "write" : "read";
+ int res;
+
+ dev_dbg(&dev->sbd.core, "%s:%u: %s %lu sectors starting at %lu\n",
+ __func__, __LINE__, op, sectors, start_sector);
+
+ init_completion(&dev->done);
+ res = write ? lv1_storage_write(dev->sbd.dev_id, region_id,
+ start_sector, sectors, 0, lpar,
+ &dev->tag)
+ : lv1_storage_read(dev->sbd.dev_id, region_id,
+ start_sector, sectors, 0, lpar,
+ &dev->tag);
+ if (res) {
+ dev_dbg(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__,
+ __LINE__, op, res);
+ return -1;
+ }
+
+ wait_for_completion(&dev->done);
+ if (dev->lv1_status) {
+ dev_dbg(&dev->sbd.core, "%s:%u: %s failed 0x%lx\n", __func__,
+ __LINE__, op, dev->lv1_status);
+ return dev->lv1_status;
+ }
+
+ dev_dbg(&dev->sbd.core, "%s:%u: %s completed\n", __func__, __LINE__,
+ op);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ps3stor_read_write_sectors);
+
+
+/**
+ * ps3stor_send_command - send a device command to a storage device
+ * @dev: Pointer to a struct ps3_storage_device
+ * @cmd: Command number
+ * @arg1: First command argument
+ * @arg2: Second command argument
+ * @arg3: Third command argument
+ * @arg4: Fourth command argument
+ *
+ * Returns 0 for success, -1 in case of failure to submit the command, or
+ * an LV1 status value in case of other errors
+ */
+u64 ps3stor_send_command(struct ps3_storage_device *dev, u64 cmd, u64 arg1,
+ u64 arg2, u64 arg3, u64 arg4)
+{
+ int res;
+
+ dev_dbg(&dev->sbd.core, "%s:%u: send device command 0x%lx\n", __func__,
+ __LINE__, cmd);
+
+ init_completion(&dev->done);
+
+ res = lv1_storage_send_device_command(dev->sbd.dev_id, cmd, arg1,
+ arg2, arg3, arg4, &dev->tag);
+ if (res) {
+ dev_err(&dev->sbd.core,
+ "%s:%u: send_device_command 0x%lx failed %d\n",
+ __func__, __LINE__, cmd, res);
+ return -1;
+ }
+
+ wait_for_completion(&dev->done);
+ if (dev->lv1_status) {
+ dev_dbg(&dev->sbd.core, "%s:%u: command 0x%lx failed 0x%lx\n",
+ __func__, __LINE__, cmd, dev->lv1_status);
+ return dev->lv1_status;
+ }
+
+ dev_dbg(&dev->sbd.core, "%s:%u: command 0x%lx completed\n", __func__,
+ __LINE__, cmd);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ps3stor_send_command);
+
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PS3 Storage Bus Library");
+MODULE_AUTHOR("Sony Corporation");
--- /dev/null
+++ b/include/asm-powerpc/ps3stor.h
@@ -0,0 +1,71 @@
+/*
+ * PS3 Storage Devices
+ *
+ * Copyright (C) 2007 Sony Computer Entertainment Inc.
+ * Copyright 2007 Sony Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _ASM_POWERPC_PS3STOR_H_
+#define _ASM_POWERPC_PS3STOR_H_
+
+#include <linux/interrupt.h>
+
+#include <asm/ps3.h>
+
+
+struct ps3_storage_region {
+ unsigned int id;
+ u64 start;
+ u64 size;
+};
+
+struct ps3_storage_device {
+ struct ps3_system_bus_device sbd;
+
+ struct ps3_dma_region dma_region;
+ unsigned int irq;
+ u64 blk_size;
+
+ u64 tag;
+ u64 lv1_status;
+ struct completion done;
+
+ unsigned long bounce_size;
+ void *bounce_buf;
+ u64 bounce_lpar;
+ dma_addr_t bounce_dma;
+
+ unsigned int num_regions;
+ unsigned long accessible_regions;
+ unsigned int region_idx; /* first accessible region */
+ struct ps3_storage_region regions[0]; /* Must be last */
+};
+
+static inline struct ps3_storage_device *to_ps3_storage_device(struct device *dev)
+{
+ return container_of(dev, struct ps3_storage_device, sbd.core);
+}
+
+extern int ps3stor_setup(struct ps3_storage_device *dev,
+ irq_handler_t handler);
+extern void ps3stor_teardown(struct ps3_storage_device *dev);
+extern u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar,
+ u64 start_sector, u64 sectors,
+ int write);
+extern u64 ps3stor_send_command(struct ps3_storage_device *dev, u64 cmd,
+ u64 arg1, u64 arg2, u64 arg3, u64 arg4);
+
+#endif /* _ASM_POWERPC_PS3STOR_H_ */
--
With kind regards,
Geert Uytterhoeven
Software Architect
Sony Network and Software Technology Center Europe
The Corporate Village · Da Vincilaan 7-D1 · B-1935 Zaventem · Belgium
Phone: +32 (0)2 700 8453
Fax: +32 (0)2 700 8622
E-mail: Geert.Uytterhoeven@sonycom.com
Internet: http://www.sony-europe.com/
Sony Network and Software Technology Center Europe
A division of Sony Service Centre (Europe) N.V.
Registered office: Technologielaan 7 · B-1840 Londerzeel · Belgium
VAT BE 0413.825.160 · RPR Brussels
Fortis Bank Zaventem · Swift GEBABEBB08A · IBAN BE39001382358619
^ permalink raw reply
* [patch 1/6] ps3: Preallocate bootmem memory for the PS3 FLASH ROM storage driver
From: Geert Uytterhoeven @ 2007-07-04 13:22 UTC (permalink / raw)
To: Paul Mackerras, Jens Axboe, James E.J. Bottomley,
Alessandro Rubini
Cc: Geert Uytterhoeven, linuxppc-dev, linux-kernel, linux-scsi
In-Reply-To: <20070704132212.726923000@pademelon.sonytel.be>
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 3162 bytes --]
Preallocate 256 KiB of bootmem memory for the PS3 FLASH ROM storage driver.
This can be disabled by passing `ps3flash=off' on the kernel command line.
Signed-off-by: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>
---
v3:
- Allow to disable ps3flash (and the preallocated 256 KiB buffer) using
`ps3flash=off'
arch/powerpc/platforms/ps3/setup.c | 31 ++++++++++++++++++++++++++++++-
include/asm-powerpc/ps3.h | 1 +
2 files changed, 31 insertions(+), 1 deletion(-)
--- a/arch/powerpc/platforms/ps3/setup.c
+++ b/arch/powerpc/platforms/ps3/setup.c
@@ -107,7 +107,8 @@ static void ps3_panic(char *str)
while(1);
}
-#if defined(CONFIG_FB_PS3) || defined(CONFIG_FB_PS3_MODULE)
+#if defined(CONFIG_FB_PS3) || defined(CONFIG_FB_PS3_MODULE) || \
+ defined(CONFIG_PS3_FLASH) || defined(CONFIG_PS3_FLASH_MODULE)
static void prealloc(struct ps3_prealloc *p)
{
if (!p->size)
@@ -123,7 +124,9 @@ static void prealloc(struct ps3_prealloc
printk(KERN_INFO "%s: %lu bytes at %p\n", p->name, p->size,
p->address);
}
+#endif
+#if defined(CONFIG_FB_PS3) || defined(CONFIG_FB_PS3_MODULE)
struct ps3_prealloc ps3fb_videomemory = {
.name = "ps3fb videomemory",
.size = CONFIG_FB_PS3_DEFAULT_SIZE_M*1024*1024,
@@ -146,6 +149,30 @@ early_param("ps3fb", early_parse_ps3fb);
#define prealloc_ps3fb_videomemory() do { } while (0)
#endif
+#if defined(CONFIG_PS3_FLASH) || defined(CONFIG_PS3_FLASH_MODULE)
+struct ps3_prealloc ps3flash_bounce_buffer = {
+ .name = "ps3flash bounce buffer",
+ .size = 256*1024,
+ .align = 256*1024
+};
+EXPORT_SYMBOL_GPL(ps3flash_bounce_buffer);
+#define prealloc_ps3flash_bounce_buffer() prealloc(&ps3flash_bounce_buffer)
+
+static int __init early_parse_ps3flash(char *p)
+{
+ if (!p)
+ return 1;
+
+ if (!strcmp(p, "off"))
+ ps3flash_bounce_buffer.size = 0;
+
+ return 0;
+}
+early_param("ps3flash", early_parse_ps3flash);
+#else
+#define prealloc_ps3flash_bounce_buffer() do { } while (0)
+#endif
+
static int ps3_set_dabr(u64 dabr)
{
enum {DABR_USER = 1, DABR_KERNEL = 2,};
@@ -175,6 +202,8 @@ static void __init ps3_setup_arch(void)
#endif
prealloc_ps3fb_videomemory();
+ prealloc_ps3flash_bounce_buffer();
+
ppc_md.power_save = ps3_power_save;
DBG(" <- %s:%d\n", __func__, __LINE__);
--- a/include/asm-powerpc/ps3.h
+++ b/include/asm-powerpc/ps3.h
@@ -427,6 +427,7 @@ struct ps3_prealloc {
};
extern struct ps3_prealloc ps3fb_videomemory;
+extern struct ps3_prealloc ps3flash_bounce_buffer;
#endif
--
With kind regards,
Geert Uytterhoeven
Software Architect
Sony Network and Software Technology Center Europe
The Corporate Village · Da Vincilaan 7-D1 · B-1935 Zaventem · Belgium
Phone: +32 (0)2 700 8453
Fax: +32 (0)2 700 8622
E-mail: Geert.Uytterhoeven@sonycom.com
Internet: http://www.sony-europe.com/
Sony Network and Software Technology Center Europe
A division of Sony Service Centre (Europe) N.V.
Registered office: Technologielaan 7 · B-1840 Londerzeel · Belgium
VAT BE 0413.825.160 · RPR Brussels
Fortis Bank Zaventem · Swift GEBABEBB08A · IBAN BE39001382358619
^ permalink raw reply
* [patch 0/6] PS3 Storage Drivers for 2.6.23, take 4
From: Geert Uytterhoeven @ 2007-07-04 13:22 UTC (permalink / raw)
To: Paul Mackerras, Jens Axboe, James E.J. Bottomley,
Alessandro Rubini
Cc: linuxppc-dev, linux-kernel, linux-scsi
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 1548 bytes --]
Hi,
This is the fourth submission of the new PS3 storage drivers:
[1] ps3: Preallocate bootmem memory for the PS3 FLASH ROM storage driver
[2] ps3: Storage Driver Core
[3] ps3: Storage device registration routines.
[4] ps3: Disk Storage Driver
[5] ps3: BD/DVD/CD-ROM Storage Driver
[6] ps3: FLASH ROM Storage Driver
They are based on:
- the current Linux kernel source tree,
- plus the PS3 patches already submitted by Geoff Levand on linuxppc-dev,
- plus the shost_priv() patch in scsi-misc (commit
bcd92c9fbcc679ee95003083056f0441a1f474fa).
All issues raised during the previous review rounds should be fixed.
There were no more comments after the third submission.
Paul already integrated Geoff Levand's PS3 patches and the first 3 patches
in this series in the for-2.6.23 branch in powerpc.git.
Maintainers of block/SCSI/misc, can you please ack the last 3 patches so Paul
can take them, too?
Thanks in advance!
With kind regards,
Geert Uytterhoeven
Software Architect
Sony Network and Software Technology Center Europe
The Corporate Village · Da Vincilaan 7-D1 · B-1935 Zaventem · Belgium
Phone: +32 (0)2 700 8453
Fax: +32 (0)2 700 8622
E-mail: Geert.Uytterhoeven@sonycom.com
Internet: http://www.sony-europe.com/
Sony Network and Software Technology Center Europe
A division of Sony Service Centre (Europe) N.V.
Registered office: Technologielaan 7 · B-1840 Londerzeel · Belgium
VAT BE 0413.825.160 · RPR Brussels
Fortis Bank Zaventem · Swift GEBABEBB08A · IBAN BE39001382358619
^ permalink raw reply
* Re: [PATCH 1/2] eHEA: Capability flag for DLPAR support
From: Jan-Bernd Themann @ 2007-07-04 12:58 UTC (permalink / raw)
To: Akinobu Mita
Cc: Thomas Klein, Jeff Garzik, Jan-Bernd Themann, netdev,
linux-kernel, linux-ppc, Christoph Raisch, Marcus Eder,
Stefan Roscher
In-Reply-To: <961aa3350707040553m12ff1bccw9421e714337c80d1@mail.gmail.com>
Hi,
good catch.
Thanks
On Wednesday 04 July 2007 14:53, Akinobu Mita wrote:
> 2007/7/4, Jan-Bernd Themann <ossthema@de.ibm.com>:
>
> > diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c
> > index bdb5241..f8c0908 100644
> > --- a/drivers/net/ehea/ehea_main.c
> > +++ b/drivers/net/ehea/ehea_main.c
> > @@ -2923,6 +2923,15 @@ static int check_module_parm(void)
> > return ret;
> > }
> >
> > +static ssize_t ehea_show_capabilities(struct device_driver *drv,
> > + char *buf)
> > +{
> > + return sprintf(buf, "%d", EHEA_CAPABILITIES);
> > +}
> > +
> > +static DRIVER_ATTR(capabilities, S_IRUSR | S_IRGRP | S_IROTH,
> > + ehea_show_capabilities, NULL);
> > +
> > int __init ehea_module_init(void)
> > {
> > int ret;
> > @@ -2937,12 +2946,20 @@ int __init ehea_module_init(void)
> > if (ret)
> > ehea_error("failed registering eHEA device driver on ebus");
> >
>
> You forgot to put:
> return ret;
> or
> goto out;
>
> > + ret = driver_create_file(&ehea_driver.driver, &driver_attr_capabilities);
> > + if (ret) {
> > + ehea_error("failed to register capabilities attribute, ret=%d", ret);
> > + ibmebus_unregister_driver(&ehea_driver);
> > + goto out;
> > + }
> > +
> > out:
> > return ret;
> > }
> >
>
^ permalink raw reply
* RE: [PATCH] ucc_geth.c, make PHY device optional.
From: Li Yang-r58472 @ 2007-07-04 13:13 UTC (permalink / raw)
To: joakim.tjernlund; +Cc: linuxppc-dev Development, Netdev
In-Reply-To: <1183539047.20673.216.camel@gentoo-jocke.transmode.se>
{snip}
>=20
> OK, but then a new property in PHY node is needed which holds
> the initial speed and duplex.
> initial-phy-capacity =3D <speed duplex> ?
> This should be useful for real PHYs too if, for instance, autoneg
isn't
> working/supported/wanted.
>=20
> Maybe it should be:
> initial-phy-capacity =3D <speed duplex autoneg>
> or
> initial-phy-capability =3D <speed duplex autoneg>
How about separate autoneg to a property "dumb-phy", which indicates the
PHY/switch doesn't provide MII register interface. Therefore, it should
use the fixed speed and duplex from device node rather than registers.
- Leo
^ permalink raw reply
* Re: [PATCH 1/2] eHEA: Capability flag for DLPAR support
From: Akinobu Mita @ 2007-07-04 12:53 UTC (permalink / raw)
To: Jan-Bernd Themann
Cc: Thomas Klein, Jeff Garzik, Jan-Bernd Themann, netdev,
linux-kernel, linux-ppc, Christoph Raisch, Marcus Eder,
Stefan Roscher
In-Reply-To: <200707041209.37127.ossthema@de.ibm.com>
2007/7/4, Jan-Bernd Themann <ossthema@de.ibm.com>:
> diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c
> index bdb5241..f8c0908 100644
> --- a/drivers/net/ehea/ehea_main.c
> +++ b/drivers/net/ehea/ehea_main.c
> @@ -2923,6 +2923,15 @@ static int check_module_parm(void)
> return ret;
> }
>
> +static ssize_t ehea_show_capabilities(struct device_driver *drv,
> + char *buf)
> +{
> + return sprintf(buf, "%d", EHEA_CAPABILITIES);
> +}
> +
> +static DRIVER_ATTR(capabilities, S_IRUSR | S_IRGRP | S_IROTH,
> + ehea_show_capabilities, NULL);
> +
> int __init ehea_module_init(void)
> {
> int ret;
> @@ -2937,12 +2946,20 @@ int __init ehea_module_init(void)
> if (ret)
> ehea_error("failed registering eHEA device driver on ebus");
>
You forgot to put:
return ret;
or
goto out;
> + ret = driver_create_file(&ehea_driver.driver, &driver_attr_capabilities);
> + if (ret) {
> + ehea_error("failed to register capabilities attribute, ret=%d", ret);
> + ibmebus_unregister_driver(&ehea_driver);
> + goto out;
> + }
> +
> out:
> return ret;
> }
>
^ permalink raw reply
* Re: [PATCH] powerpc: Add of_register_i2c_devices()
From: Segher Boessenkool @ 2007-07-04 12:19 UTC (permalink / raw)
To: Guennadi Liakhovetski; +Cc: linuxppc-dev, Paul Mackerras, i2c
In-Reply-To: <Pine.LNX.4.60.0707040707010.4300@poirot.grange>
>> Now some OF I2C code goes looking for IIC devices in the
>> device tree. It finds this thing, and from a table or
>> something it derives that it has to tell the kernel I2C
>> layer this is an "rtc-rs5c372".
>
> (I2C ML cc'ed.)
>
> This is where I WOULD disagree. These tables would rather live
> inside the
> i2c layer,
Physical location doesn't matter, logical location is
a separate layer.
> be filled by respective drivers themselves.
That would be nicer yes, but a bigger change.
> Noone except the
> rs5c372 driver can know which devices it can handle.
I don't really agree but that's more a philosophical than
a technical argument.
> Using the very same
> your argument - what if in a future version this driver disappears and
> another one will be used for these devices? Then that driver will
> have to
> register support for this device.
And it's all in the same kernel source tree so it would be
a trivial fixup -- quite different from keeping OF device
trees and Linux kernels in synch (which is pretty much
impossible).
> For this to work i2c would need something similar to what pci, usb
> do -
> register supported device ids. The only difference is that instead of
> numerical IDs we have to use plain text names for i2c devices...
Yeah, that would be nice. Are you suggesting the Linux I2C
layer should use "OF-style" names as its "native" naming
scheme? I'd rather keep the namespaces separate, coupling
them always seems like a great idea and always turns out a
disaster.
>> [It would be nicer if it
>> could just instantiate the correct driver directly, but
>> if that's how the Linux I2C layer works, so be it].
>>
>> No change in the I2C "core" needed, just an OF "compatible"
>> matching thing like is needed *everywhere else* too.
>
> Yes, this is why I put "would". Looks like this is the common powerpc
> practice ATM - to make such a glue to map arbitrary "OF names" to what
> respective drivers react to. Like in the case of the serial driver.
Yes. This is the most flexible scheme possible and allows
for all kinds of fixups/workarounds in case of broken device
trees (or broken kernel code, for that matter).
> But -
> i2c is much more diverse and dynamic than serial, so, maybe it is
> worth
> thinking about "fixing" i2c?
Be my guest :-) I care more about the OF side of things, but
let me ask anyway -- what do you see as "broken" in the Linux
IIC "core" that needs fixing here?
Segher
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox